Skip to content

Instantly share code, notes, and snippets.

@CrossEye
Last active December 19, 2015 17:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CrossEye/5993043 to your computer and use it in GitHub Desktop.
Save CrossEye/5993043 to your computer and use it in GitHub Desktop.

Interesting and yet frightening technique

Run the following code against Ramda.

// Simple predicate to test whether a number is even
var even = function(n) {return (n % 2) === 0;};

// Generates an infinite list of Fibonacci numbers
var fibsgen = generator(
    [0, 1],
    function(p) {return p[0];},
    function(p) {return [p[1], p[0] + p[1]];}
); // generator: [0, 1, 1, 2, 3, 5, 8, ... ]

// Extracts the first 20 elements of that list
var fibs20 = fibsgen.take(20); // plain list: [0, 1, 1, 2, 3, 5, 8, ... 4181]

All these techniques work well against both the infinite list and the finite one to get the first 5 even Fibonacci numbers:

take(5, filter(even, fibsgen)); //=> [0, 2, 8, 34, 144]
take(5, filter(even, fibs20)); //=> [0, 2, 8, 34, 144]

pipe(filter(even), take(5))(fibsgen); //=> [0, 2, 8, 34, 144]
pipe(filter(even), take(5))(fibs20); //=> [0, 2, 8, 34, 144]

compose(take(5), filter(even))(fibsgen); //=> [0, 2, 8, 34, 144]
compose(take(5), filter(even))(fibs20); //=> [0, 2, 8, 34, 144]

But the following technique only works agains the generator. We don't try to wrap Arrays with all the list functions from Ramda, so we can't usually chain methods in this fluent style:

fibsgen
    .filter(even)
    .take(5); //=> [0, 2, 8, 34, 144]

But there are clearly times when this is the sort of interface desired, even for plain list implementations But a technique from Hugh Jackson can get us reasonably close. Just add tap to Object.prototype like so:

Object.prototype.tap = function (fn){ return fn(this.valueOf());};

Then you can use it like this:

fibs20
    .tap(filter(even))
    .tap(take(5)); //=> [0, 2, 8, 34, 144]

His article goes further, and describes how to create a version that allows for additional arguments to be passed through. He discusses the reasons people don't like to extend Object.prototype and makes a case for this being an exception; his production implementation is more of a mixin than a simple extension of Object.prototype, but still to be widely useful, you'd probably have to apply it to that prototype, so it's not clear how much help that is, but at least he's taking the objections seriously.

I feel I should be horrified by this. I'm more than a little uncomfortable to discover that I'm not horrified in the least, just intrigued!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment