Skip to main content

News

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

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Link.js v0.4.2
Link.js v0.4.2

GitHub Page: https://github.com/Radnen/link

Link.js is a very fast, lazy executed, query library. Inspired by Lazy.js and LINQ, it aims to make data manipulation easier to write and fast to execute.

Examples:
Code: (javascript) [Select]

// get n'th even number:
var numbers = [0, 1, 2, 3, 4, 5];
var n = Link(numbers).where(function(item) { return item % 2 == 0; }).get(3).toArray(); // returns [4]

// get enemy with lowest health:
var enemies = [{hp: 20}, {hp: 35}, {hp: 15}, {hp: 40}];
var n = Link(enemies).sort(function(a, b) { return a.hp - b.hp; });

// get a person on the map at a location (assume the x, y, and tile function are defined):
var n = Link(GetPersonList()).where(function(name) { return GetPersonTileX(name) == x && GetPersonTileY(name) == y; }).first();

// see if a particular person exists:
var n = Link(GetPersonList()).where(function(name) { return name == "player"; }).length() > 0;


With TypeScript things look a lot closer to LINQ (and more compact!):
Code: (javascript) [Select]

var array = [1, 2, 3, 4, 5, 6, 7, 8];
var evens = Link(array).where(n => n % 2 == 0)->each(n => Print(n));


Have fun writing your queries!

You may need Function.bind(), especially for legacy Sphere versions. For those using minisphere, you don't have to worry.

Code: (javascript) [Select]

Function.prototype.bind = function (self) {
    var args = Array.prototype.slice.call(arguments, 1), func = this;
    return function () {
        return func.apply(self, args.concat(Array.prototype.slice.call(arguments)));
    };
};
  • Last Edit: November 12, 2016, 12:59:10 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
  • miniSphere Developer
Re: [code] Query.js for Sphere
Reply #1
Neat, it's like LINQ for JavaScript. ;D

Although, doesn't JQuery already do all this?
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: [code] Query.js for Sphere
Reply #2
I took inspiration from LINQ and jQuery, yes, but this isn't geared for the web, and so I think can be more applicable for Sphere games than jQuery.
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

  • DaVince
  • [*][*][*][*][*]
  • Administrator
  • Used Sphere for, like, half my life
Re: [code] Query.js for Sphere
Reply #3
Looks useful, good work! Another one of those things I'd use if I made a bigger game in Sphere. :)

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: [code] Query.js for Sphere
Reply #4

Looks useful, good work! Another one of those things I'd use if I made a bigger game in Sphere. :)


I actually did use it in a small game, my LD28 game (a simpler version of this). I needed something to elegantly search through an array of people or enemies, and I kept using for loops to do it. Then I realized, what if Sphere had a LINQ-like thing or a jQuery-like cascading function call thing? That means I could then do searches in a single line of code, lifting the complexity entirely.
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

Re: [code] Query.js for Sphere
Reply #5
Makes you think.

I usually try to organize data so that it is easily searched using loops. But maybe that's why I have a hard time thinking of a simple way to use this--I'm inadvertently making things harder to do this way.

  • Fat Cerberus
  • [*][*][*][*][*]
  • Global Moderator
  • miniSphere Developer
Re: [code] Query.js for Sphere
Reply #6

Makes you think.

I usually try to organize data so that it is easily searched using loops. But maybe that's why I have a hard time thinking of a simple way to use this--I'm inadvertently making things harder to do this way.


The problem with that approach is that you have to prioritize certain data over other for optimization purposes, and once your dataset gets large enough and your types of queries varied enough, you end up with all kinds of conflicts of interest when trying to do said optimization.  That is why LINQ was invented in the first place.  Better just to lay out the data in a way that makes sense and use a query library like this one when you need to sift through it.

I may have to find a use for this in Specs when I ever find the initiative to work on it again.  How is performance with this, Andrew?
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: [code] Query.js for Sphere
Reply #7

I may have to find a use for this in Specs when I ever find the initiative to work on it again.  How is performance with this, Andrew?


The performance is not as good as hand-rolling your own loops. Look at this:

Code: (javascript) [Select]

Query.addProcedure("getAt", function(obj, x, y) { return GetPersonTileX(obj) == x && GetPersonTileY(obj) == y; });
Query.addProcedure("get", function(obj, match) { return obj == match; });
var n = Query.from(GetPersonList()).get("player").getAt(x, y).exists();


versus the hand-rolled version:
Code: (javascript) [Select]

function IsPersonAt(name, x, y) {
    return GetTileX(name) == x && GetPersonTileY(name) == y;
}


So in fact that query is much more inconvenient than the function call. (So probably was a bad example). But notice if yu want different behaviors you have to rewrite the for loop each time, causing "code explosion" (writing similar bits of code to do slightly different queries because you can't just change the conditionals each pass).

But, for a decent example like this:

Code: (javascript) [Select]

// get all enemies whose hp is lower than a specified amount, and make them run away:
var enemies = [{hp: 20}, {hp: 35}, {hp: 15}, {hp: 40}];
Query.addProcedure("getLow", function(e, match) { return e.hp <= match; });
var n = Query.from(enemies).getLow(20).foreach(function(e) { e.runaway(); });


versus this:

Code: (javascript) [Select]

// get all enemies whose hp is lower than a specified amount, and make them run away:
var enemies = [{hp: 20}, {hp: 35}, {hp: 15}, {hp: 40}];
function Runaway(hp)
{
    for (var i = 0; i < enemies.length; ++i) {
        if (enemies[i].hp <= hp) enemies[i].runaway();
    }
}


We see the for loop version is still faster since we can pack in a lot during a single for loop. But it get's more complex, and harder to read. And due to code explosion, the query can be rewritten a hundred times, but new for loops need to be written to do other things. Query is not a speed tool, it's a neatness tool. The queries themselves are supposed to look better and read easier than the for loops that you would generate from them. Also, with less for loops, you lessen the overall sources of error.

Lets put it this way: it shines best during turn based battle systems, and for a battle system with less than a hundred opponents on either side, performance is not a concern. I'd gladly take a small performance hit if it does two things:

1. drastically reduce the overall LOC.
2. drastically increases readability.

Which both ups maintainability. ;)
  • Last Edit: December 21, 2013, 12:46:07 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
  • miniSphere Developer
Re: [code] Query.js for Sphere
Reply #8
Yeah, I didn't expect it to be faster than a purpose-built for loop, that would be asking for the impossible.  :D  I just meant would the performance hit be crippling in any real world scenarios, which you seem to think no, so that's good to know. :). Not worth suffering from massive lag for a little convenience.
miniSphere 5.0.1 - Cell compiler - SSj debugger - thread | on GitHub
For the sake of our continued health I very much hope that Fat Cerberus does not become skilled enough at whatever arcane art it would require to cause computers to spawn enourmous man eating pigs ~Rhuan

Re: [code] Query.js for Sphere
Reply #9
So this is kinda like Underscore, but optimized for Sphere? Neat. I should check if stock Underscore works with Sphere. Either way, I do like the LINQ-iness of this. I'm no fan of C# but LINQ is pretty cool.

A couple of things I noticed browsing through the source:

  • if (!(typeof func == "function")) really needs to be if (typeof func != "function"). The former is incredibly ugly and less obvious.

  • Why is foreach neither forEach like in vanilla JS nor each like in most libraries? (jQuery, MooTools, Underscore and derivatives, and a bunch of more obscure ones.)

  • length() could, and probably should, be a getter/setter. Both SpiderMonkey and V8 support __define(G|S)etter__, and if you want to be future-proof you could check for Object.defineProperty().

  • Not related to the source, but you should probably remove the "efficiently" from "edit or modify efficiently" in the post. Query is very inefficient.


I highly recommend checking out Lazy.js. Not only does it offer a similar syntax, it is much more powerful and has performance that meets or exceeds plain for loops. It should run in Sphere, although I haven't tried it yet. The downside is it's slightly less trivial to extend to incorporate game-specific methods like getAt().

This is kinda useful, but IMO it's just another example of NIH syndrome in the Sphere community. It just doesn't do things as well as Underscore, Lo-Dash, or Lazy.js.
  • Last Edit: December 28, 2013, 02:35:47 am by alpha123

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: [code] Query.js for Sphere
Reply #10
Well part of anything NIH, is learning how to make one. ;) During the compo I didn't want to work with large, ugly codebases that may only half work (well if they were browser-centered, that is). I needed something I could understand and use, for fun. And I thought the community would like to see it.

1. Ok.
2. foreach is a C# keyword, I've grown to spell it that way.
3. Ok. (__define(G|S)etter__ are not ECMA standard though, but there are other ways.)
4. Efficiency is in coding time, not execution speed, I should make that clear. Ha!

Edit:
Well, I looked into Lazy.js and it looks awesome! Feature rich with a cool implementation method. :) But it is too much for small games to put a 5300 line file into a project (though I suppose one can just use the minified version). I think Lazy.js would work right out of the box though.
  • Last Edit: December 28, 2013, 03:46:56 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

Re: [code] Query.js for Sphere
Reply #11
That is a very valid point. I certainly have "invented" something that already exists to learn. I'm just not sure that the community needs another library that effectively reimplementes established libraries, only with fewer maintainers and an inferior (although dramatically simpler) implementation. Query does have the advantage of being built to be extended with game-specific functions though.

Haha, I didn't remember Lazy.js is 5.3k lines. :P That may be just a teeny bit overkill for small games....
Lazy.js is definitely one of the coolest JS projects I've seen. I'm not really into lazy-evaluation type stuff (I have definitely backed off on hardcore functional programming), I just like how useful it is and how it manages to be quite efficient.

An interesting experiment would be to take the ideas from Lazy.js and implement just the barebones features that can then be extended with game-specific methods. That is basically just take Query's concept and adjust the implementation using ideas from Lazy.js.


  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: [code] Query.js for Sphere
Reply #12
Alpha: worrying about efficiency and the fear of inferiority in your or other peoples code can make you quite simply unhirable. ;) Then again, if speed is critical; complain away!


An interesting experiment would be to take the ideas from Lazy.js and implement just the barebones features that can then be extended with game-specific methods. That is basically just take Query's concept and adjust the implementation using ideas from Lazy.js.


The thought definitely passed my mind, twice. I'll take a deeper look into Lazy. But the concept shouldn't be too hard: create iteration points when doing 'transforms' on the underlying data, and only iterate when iteration needs to happen.

Effectively you take functions like map, where, sort, etc. and compile them into one single pass. Then when you need to access the data after the end of a query, you run all those piled-on functions, and then retrieve the data. This effectively emulates those hand-written for loops that does everything in one shot. But it is slower than those hand-rolled functions due to abstraction overheads, but much faster than n loops for n operations. (Woah... This sounds similar to what an SQL optimizer aims to do with certain operations.)

It's one of those things that you don't see, and when you do it's like lights going off. Because Lazy.js is exactly what the description of Query is trying to do: Emulate what you'd do for hand-written for loops, but make it reusable and extensible.
  • Last Edit: December 29, 2013, 03:46:06 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

Re: [code] Query.js for Sphere
Reply #13
Warning: longish read ahead, probably disorganized thoughts. Readers should continue at their own risk.

I am not to be held responsible for any spontaneous sleeping, confusion, or other mental or physical harm that may come to the party privy to this information (henceforth known as the "reader") as a result of reading the forthcoming paragraphs nor shall I be liable for ego harm due to disagreements with the opinions contained therein, which do not necessarily reflect the views of the Sphere or web communities and are solely the views of the author, who does not intend damage to any parties involved but is not culpable for damages including but not limited to the aforementioned afflictions or any of the following such as damages to the reader's keyboard, damages to the reader's cat sitting on the reader's keyboard, potentially useless knowledge gained from the text, or eye strain caused from reading small text.


Alpha: worrying about efficiency and the fear of inferiority in your or other peoples code can make you quite simply unhirable. ;) Then again, if speed is critical; complain away!

Given that I'm currently hired quite happily I'm relatively sure that's not an issue. Disclaimer: I work as a web designer. Which is what I've always liked to do. I'm not a particularly good programmer (despite how much it may seem that I criticize your stuff, I'm quite confident you are a much better programmer) especially in anything except JavaScript and Ruby. :P I'm just kinda picky and know JS really well. Hence hopefully constructive criticism (really I suppose I should just skip critiquing bits of code and write a JS tutorial). Like it or not, the quality of JS in the Sphere community is far, far less than that in the web community. That said, yours is generally pretty good.

...Anyway, with that out of the way, I've worked on too many projects that have simply not cared at all about efficiency. Making something more efficient is way harder after it's written and especially if the original developer leaves. I prefer to put a lot of thought into it initially and come out with something both correct and efficient. It takes longer to get something working, but in my opinion (which should probably be taken with a tablespoon of salt) definitely pays off on in the long run.

I feel a little hypocritical right now since my favorite languages are Lua, Ruby, Io, and JavaScript.... xP (Note to readers: these are among the slower languages currently (except Lua which is pretty fast).) My excuse is speed is not necessarily the same as efficiency.

In summary, I just feel that people place too little value on efficient code. In Query, something as simple as Query(nums).filter(function (n) n % 2 == 0).map(function (n) n * 2).reduce(function (a, b) a + b) has to iterate through the array three times. Sure, that's quite fast on modern computers, but Query is a relatively low-level library intended to be used by games. Games tend to have rather strict performance requirements and may involve large arrays.

[/rant]

Quote

The thought definitely passed my mind, twice. I'll take a deeper look into Lazy. But the concept shouldn't be too hard: create iteration points when doing 'transforms' on the underlying data, and only iterate when iteration needs to happen.

The real problem that I can think of is making it easy to extend. Ideally the extension author shouldn't have to worry too much about the lazy evaluation, but I'm not sure that's possible to avoid.

Quote

It's one of those things that you don't see, and when you do it's like lights going off. Because Lazy.js is exactly what the description of Query is trying to do: Emulate what you'd do for hand-written for loops, but make it reusable and extensible.

You do "see" Lazy.js's work somewhat in that it requires the each() method to actually do anything. That's rather negligible though. IIRC Lazy.js also has some method to convert the internal sequence to an array, as opposed to just being able to use the result directly.

  • Radnen
  • [*][*][*][*][*]
  • Senior Staff
  • Wise Warrior
Re: [code] Query.js for Sphere
Reply #14
Alpha:
Well, part of the issue is for Query-like libraries you didn't even know efficiency until you saw Lazy.js, and how dare you compare my code to that behemoth! (I'm but a single man, in a single field.) Query.js is a heck of a step in the right direction than a lot of code others write for their Sphere games, and I intend for it to really help people write less code and do more. Perhaps raw speed it has not, but the fundamental design pattern is really inspiring. I should also mention nobody wants to hear that their code is inferior... I didn't even know Lazy.js existed, so excuse me for not making it The Best Way Possibleā„¢.

Also, as a slight nitpick I think your words sting a lot since you put adjectives like "incredible" before many words that makes them feel stronger than they are. Incredibly ugly, stupid etc. sounds harsher than a half-dozen other ways of writing something. It's like that quote from the Big Lebowski: "You're not wrong Walter. You're just an asshole".


My excuse is speed is not necessarily the same as efficiency.


I agree! But, you are right games do require raw speed too. But I think I got that covered with a compiled source like SSFML, or Jest's TurboSphere. Anyways, I am actively looking into Query.js to try and speed it up.

/No hard feeling's man.
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