Skip to main content

News

Topic: Link.js v0.4.2 (Read 95725 times) previous topic - next topic

0 Members and 3 Guests are viewing this topic.
  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.3.0
Reply #270
Good news: Link will officially be part of the standard library in minisphere 4.0. ;D   How you will use it:

Code: (javascript) [Select]

// at top of file (usually):
const link = require('link');

// Link query
tactics = link(allTactics)
    .where(function(it) { return it.target.isAlive(); })
    .where(function(it) { return it.moveIndex < it.moves.length; })
    .toArray();
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.4.0
Reply #271
I've updated Link to 0.4.0

Changes


  • Added Cross Product

  • Added LinkException

  • Parameters that take arrays, can now also take link contexts.



The Cross Product:
Code: (javascript) [Select]

var items1 = ["A", "B"];
var items2 = ["C", "D"];
Link(items1).cross(items2).each(function(item, index) {
    Print("Item at: "+ index);
    Print(item); // e.g: ["A", "C"] or ["B", "D"]
});


Index is the number 0..n where n is the number of total items in the final list after the cross product has resolved.
Item is an array whose contents [0..m] is the m times of cross products you do. In the above example, it will contain an array with indices at [0, 1].

You might have noticed in another thread here I had used item.A, item.B, and item.C to denote the items that result from the cross product, for each join. But I deemed the internal method to figure that out too slow. A basic array with the items is sufficient enough. It's now [item1, item2, itemN, ...]
  • Last Edit: July 27, 2016, 01:26:58 am by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.0
Reply #272
It occurs to me that cross() could probably be used to implement matrix multiplication.  Not very practical (there are much more efficient ways to do it), just a curiosity.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.0
Reply #273
Quick suggestion: Rename .each() to .forEach() for consistency with JS array.forEach and also highlighting that it's an endpoint and not lazy like the rest of the chain.  And then .execute could be named .each.  This way it's easy to tell that the two methods do the same thing, the only difference is lazy vs. not lazy.

edit: I found a way to use Link to work directly with objects and query both the keys and values:
Code: (javascript) [Select]

link(Object.keys(tests))
.where(function(key) { return key.substring(0, 4) === 'test'; })
.where(function(key) { return key !== 'test' })
.map(function(key) { return tests[key]; })
.each(function(test)
{
if (typeof test === 'function')
test();
else if (typeof test === 'object' && test !== null)
run(test);
});
  • Last Edit: November 10, 2016, 02:19:28 pm by Lord English
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.4.0
Reply #274
Alright, I added a .forEach alias so it can relate to other libraries. I didn't want to make each become execute or an alias of it because it may break a lot of existing code (I use this library at work professionally, and so do my coworkers, lol).

Your keys method got me thinking and I added a chainable .keys() method which basically enumerates through all of the keys in an array of objects, or you can use pluck to narrow down on to a single object in which to enumerate it's keys. And if you keep a reference to that object, you may decide to retrieve it's values. This method is still, I shall say, experimental.

Example:
Code: (javascript) [Select]

var obj = { a: 1, b: true, c: "hi" };
Link([obj]).keys().forEach(function (key) { console.log(key); }); // "a" "b" "c"
Link([obj]).keys().forEach(function (key) { console.log(obj[key]); }); // 1 true "hi"
  • Last Edit: November 12, 2016, 01:02:41 am by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.0
Reply #275

Alright, I added a .forEach alias so it can relate to other libraries. I didn't want to make each become execute or an alias of it because it may break a lot of existing code (I use this library at work professionally, and so do my coworkers, lol).


That's okay, I might do some tidying up for the version of Link included with minisphere to make things more elegant since Sphere v2 hasn't been finalized yet so I don't have any API compatibility promises in place yet. ;)  But I know what you mean about everyone depending on it, Link is incredibly useful.  Every time I go to write a new miniRT module now the first thing I end up doing is require('link') and I always find some use case for it.  I rarely write for loops in JS anymore. :D

But yes, just seeing something like Link(obj).where(...).each(...) makes me think that the each is actually lazy when it's not.  forEach() makes it clear that we're running the query at that point and iterating on the result.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.2
Reply #276
@Radnen: Hm, it looks like the latest changes didn't actually make it into 0.4.2?  The last commit is a README edit from July.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.4.2
Reply #277
I think I forgot to push the source with github for windows (ugh!) it keeps updating and I completely spaced out on the 'sync' button. I usually use git solely from command line, and so I just now pushed it via console command line and now everything's good to go!
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.2
Reply #278
That .keys() method is nice.  A feature suggestion for the future: If user passes a non-array object directly to Link() constructor, apply .keys() automatically, and maybe pass the key and value to control functions (value first for compatibility with array behavior):

Code: (javascript) [Select]
// note: ES6 notation because it's more concise
Link(obj).forEach((v, k) => console.log(`${k} = ${v}`));


That syntax would be very elegant: "for each 'v' with key 'k', print k and v"

edit: Oh, and it also avoids an extra .map() if you need to work with the values instead of the keys.
  • Last Edit: November 12, 2016, 10:35:56 am by Lord English
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.4.2
Reply #279
The second argument is already the index, and it can be tricky to assume the array will have objects with keys, because you could be iterating through numbers, or even a string such as Link('hello world!').forEach().

I'd have to add another modifier such as .keyValue() or have keys() itself permanently alter the .each/.forEach parameters...
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.2
Reply #280
So I got bored and decided that as a learning experience I should try reimplementing Link from scratch.  The result is this experimental "from" module that natively allows querying objects:
https://github.com/fatcerberus/minisphere/blob/master/assets/system/modules/from.js

Code: (javascript) [Select]

const from = require('from');

// ES6 syntax
var food = { pig: "people", cow: "kitties", ape: "bananas" };
var result = from(food)
    .where((v, k) => k === 'pig')
    .besides((v, k) => console.log(`${k} = ${v}`))  // debug
    .select((v, k) => "812 " + v);

console.log("Which animals survived the eating? " + Object.keys(result));
console.log("What did the pig eat? " + result.pig);


select works like a combination of map and toArray in Link.  If no mapping function is provided it selects the original value (map itself is still available and is chainable).  And besides is like execute: it calls the function and then passes through the original value, ignoring the return value.

No idea how performance compares to Link, though.  There's no manual optimizations, I just implemented things in whatever way seemed most obvious.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.4.2
Reply #281
Ok, that's cool. So you've basically reimplemented link just so you can have value, key pairs in the callback functions, and you seem to make use of more ES6 features.

There's this bit I'm curious about:
Code: (javascript) [Select]

var obj = Reflect.construct(LinkObj, arguments);


This is interesting, because I couldn't find another function that's similar. Using new and apply() doesn't work since Apply will destroy the original constructor. I had the same problem before and simply used arg1, arg2, arg3 manually because there wasn't a decent way of trying to get the arguments to pass to an object instantiation via 'new'.

But I've held off on ES6 for link, and I'm sure my runners and other methods could be simplified a lot by using these new ES6 API's especially the Reflect object.
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.2
Reply #282
There's the key/value thing but I also did it as a way to understand how Link actually worked.  I was never able to fully grasp exactly how your chain system worked which made me hesitant to try to make modifications.  Now that I actually programmed something like it I understand how things work a lot better.

Reflect.construct() is indeed nifty.  You can polyfill it for ES5 but it's a bit obtuse.  Basically you do new (constructor.bind.apply(undefined, args)).  The ES6 syntax is much nicer though. :)

One thing I changed over your design was to distinguish chainable actions ("links") from endpoints ("ops").  This allowed me to reuse a lot of code.  It turns out that almost everything can be implemented in terms of map so there was no need to have a bunch of different link types.
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: Link.js v0.4.2
Reply #283

It turns out that almost everything can be implemented in terms of map so there was no need to have a bunch of different link types.


Yeah, many other systems stop there. Map is everything. Well, technically, reduce, filter and map are the three pinnacles of functional programming since everything you do can map down to one of those three (I even used the word map in that sentence, so they technically reduce to just map). I just add more options to the table since some things make more intuitive sense when everything's not simply 'map'. Because map, and filter is so vague... what exactly does 'unique' do? Well, it's no different than a filter, but it makes sure it produces just the unique results. What does pluck do? Well, it retrieves a certain property, which you could have just done with a basic map.

Reduce is interesting since there is leftReduce and rightReduce (leftReduce is simply 'reduce' and rightReduce is not implemented in Link). It's a combiner.

Code: (javascript) [Select]

Link([1, 2, 3]).reduce((a, b) => a + b).each(console.log); // 6


It looks at 1, and 2 and does the mapping function. Then it takes that result and looks at 3 and does the mapping function again. .each() is not the same thing since it looks at only one item at a time, in effect, it's not "reducing" the size of the set, like how the above operator did. Hashing algorithms and checksums are often implemented this way.

Edit:
rightReduce puts the result i n 'b' while leftReduce puts the result in 'a'. Right reduce is intended to work backwards in a list, which is not easy to achieve in link since it only goes forwards.

Link.js idea: make an option for it to go backwards... Could be interesting...
  • Last Edit: November 13, 2016, 09:29:26 pm by Radnen
If you use code to help you code you can use less code to code. Also, I have approximate knowledge of many things.

Sphere-sfml here
Sphere Studio editor here

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • Sphere Developer
Re: Link.js v0.4.2
Reply #284
Yeah, the extra conveniences are a big part of what keeps me using link.  Especially since ES5 doesn't have arrow functions, it's much easier to figure out what pluck('name') does vs. an equivalent map operation.  It's a pain to do functional programming in pre-ES6 JavaScript because lambdas are so damn verbose.

I'm experimenting with simplifying things some more for my experimental "from" script.  One thing I notice that link does is to call the points recursively.  While this is in line with the chain metaphor, it ends up duplicating effort because every single point (except endpoints) needs to have a call to this.next.exec().  My idea was to instead have points return true to continue, and false to drop the current value, filtering it out.

Endpoints would work similarly - return true to continue with the next value, or false to short-circuit.  I think this is overall a better separation of concerns.

One thing I'm having difficulty understanding is how link adds items to a query midstream.  For example link(a1) starts out with all of a1 but then you can tack on .cross(a2) and get a bunch of permutations.  Not sure how that works.  Also what does coalesce do?  It says it merges modified results back into the array, but I'm not sure what that means in practical terms.  Link already has update()...
neoSphere 5.9.2 - neoSphere engine - Cell compiler - SSj debugger
forum thread | on GitHub