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:
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:
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:
// 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:
// 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.