Expressive JavaScript

August 2015

JavaScript seems to be looking more than just a little towards Python when it comes to digging up new stuff to implement in the language, and it makes me happy. To name a few:

I am not saying these things aren’t used in other languages, just that I get a certain pythonic vibe from what’s going on, and I am delighted by it. I would much rather see JavaScript turn into Python than into Java.

Python is a language to get stuff done in, and in my opinion so is JavaScript. Time from idea to mockup, even production, is short; and we ought to value that it.

Here is an example of “JavaScript” code that I found in the excellent library Trine by Jussi Kalliokoski. Specifically it is the function take:

/**
 * Yields the items that qualify the condition.
 *
 * @this {Iterable<T>}
 * @param condition A condition function to see if the item should be taken.
 * @ntime O(n)
 * @dspace O(1)
 * @example Basic Usage
 *
 * ```javascript
 * [1,2,3,4]
 *   ::take(function () {
 *     return this % 2 === 0;
 *   }) // yields 2 and 4
 * ```
*/
export function * take <T> (
    condition : (_this : T) => boolean,
) : Iterable<T> {
    for ( const item of this ) {
        if ( item::condition() ) { yield item; }
    }
};

In case you don’t know, item::condition() is the same as condition.call(item). Now, can you read and understand this code? What if I told you there is a bug in it (there isn’t… I think), would you be comfortable finding and correcting it? I am thinking about the verbosity of it. Every single function in the library looks like this, and to be honest I could not read it at first. Even finding the body of the function had me pausing for a second.

Now I don’t mean to belittle this work! Not at all. The code is diligent, tested, extremely well documented (when was the last time you added a dspace to your docs?), and even has types in place; and the idea of the library itself is brilliant (to implement utility functions that work on this, so you can call them method-style from anything using ::). However, this is not the kind of code that I enjoy reading, nor writing. I find it ridiculously difficult to look at the code, and conclude that it is essentially a filter.

Same code, with no cruft:

function* take (fn) {
    for (let v of this)
        if (v::fn()) yield v;
};

Or without the this and :: (and let’s call it what it is):

function* filter (fn, it) {
    for (let v of it)
        if (fn(v)) yield v;
};

A third time, this time with comprehensions:

const filter = (fn, it) =>
    (for (v of it) if(fn(v)) v);

This is what I like to see. This makes me feel all giddy inside. Let’s implement map too!

const map = (fn, it) =>
    (for (v of it) fn(v));

Simple. Beautiful. If you have Firefox installed then you can literally copy/paste the above function into the console (leave out the const, it’s not supported yet). Something like this:

var map = (fn, it) => (for (v of it) fn(v));
// → function map()
[...map(n => n+1, [1, 2, 3, 4])]
// → Array [ 2, 3, 4, 5 ]

It’s that simple. Notice that you can also write the map as…

var map = (fn, it) =>
    [for (v of it) fn(v)];

…if you want it to return an array instead.

As much as I prefer functions for everything, I have to say that I find this amazing.

So what about types? I like types, but not in the code. Look at the above function, and tell me that the types can not be inferred. They can, and Facebook is doing an amazing job of that with Flowtype.

Even though I consider these kinds of utility libraries less and less useful (as expressiveness goes up), I wrote a small utility lib called iters.js, consisting of all the usual suspects when it comes to util libraries, plus a few from the Python built-in functions, such as xrange (I just called it range). I wrote it not for you to use in production and write up issues on github (though, feel free), but rather, it was a mini-curiosity project where I got deeper into working with iterators and got a taste of the expressiveness coming in the near future.

I recommend it. It made me love what I’m seeing!