Yeah, my library is a pain to add things on to it. It's very "class heavy" - like how compilers are written. So adding a new feature means adding a lot of little stuff, most of which is boilerplate stuff that I can't seem to get rid of effectively.
I know how that goes. My library pretty much
is a compiler, actually (and a reasonably advanced one at that, capable of function inlining, loop unrolling, and specializing certain chains of functions). It does make adding new methods pretty tedious. To do your example, it would be
For.prototype.add = function (num) {
this.nodes.push(new AddNode(num))
return this
}
function AddNode(num) { this.num = num }
AddNode.prototype.compile = function () {
return 'a+=' + this.num + ';'
}
For([1, 2, 3]).add(1).toArray() // [2, 3, 4]
which is surprisingly not all that different. It is, however, fairly tedious, so I developed an easier (but less performant) way to extend it by taking advantage of the fact that every array operation
1 can be done as a map, filter, or reduce.
2For.addFilter('even', function (element) { return element % 2 == 0 })
For.addMapper('add', function (element, index, amount) { return element + amount })
For.addEvaluator('sum', function (accumulator, element) { return accumulator + element })
For.range(10).even().add(5).sum() // 45
All these methods do is create a function on
For.prototype that does all the boilerplate stuff, and then calls map, filter, or reduce with a function that does more boilerplate and then calls the actual extension function.
There is another extension method,
addFilterMap, which is a little more complicated (it generates a sequence of method calls instead of just a single one to map/filter/reduce) but allows both transforming and testing an element at the same time.
The above is how 80% of the library is implemented; which is why you can see how it is so fast. It runs at nearly the speed of traversing your standard every-day link list. That's the whole secret. I really want to expand to a binary tree, but such a format is not always easy to use when given an already linear array, but my hope would be to add binary searching to it - crazy stuff for a 0.3.0 release someday in the future.
I really like how that's implemented; it is simple, effective, and is very friendly to modern JS engines which will do a lot of optimization for you. It really is very fast too; I was only able to beat Link in Chrome by less than 10 milliseconds when I added loop unrolling. I still haven't beaten Link in Firefox, and this is in a test that is rather contrived to play off of my library's specific optimizations.
The binary tree thing would be quite interesting. I was thinking of extending my library to binary heaps and linked lists, but that would A) add a lot of complexity and B
) I don't really know enough about data structures or algorithms to do that effectively.
Also, brony alert! (Bronies are cool, I bear no ill will against them )
Oh, alright, you found me.
1With some exceptions like drop and take. For/Dash/name-TBD's extension system doesn't quite support extending with methods like that yet, but the general groundwork is there.
2Technically I suppose map and filter can be implemented on top of reduce, but that would kill For's performance.