ECMAScript 6 Made My Code Beautiful

I’ve always loved the flexibility of JavaScript. Don’t get me wrong I’m all for type safe languages with defined modes of failure, it’s just that the ‘hackier’ languages provide a sense of freedom and exploration that make them a joy to work with. JavaScript is just such a language, it’s flexibility allows you to write arbitrary code with just six characters, and the particular combination of tricks that have arisen due to the rather unique history of the language make it ideal for code golfing.

That said when I tried to write currying I found that for a language with a great amount of flexibility there wasn’t a pleasing and concise way to write it. Searching “JavaScript Currying” on Google gave me the website svendtofte.com, which had the following excerpt:

function curry(func,args,space) {
var n = func.length - args.length; //arguments still to come
var sa = Array.prototype.slice.apply(args); // saved accumulator array
function accumulator(moreArgs,sa,n) {
var saPrev = sa.slice(0); // to reset
var nPrev = n; // to reset
for(var i=0;i<moreArgs.length;i++,n--) {
sa[sa.length] = moreArgs[i];
}
if ((n-moreArgs.length)<=0) {
var res = func.apply(space,sa);
// reset vars, so curried function can be applied to new params.
sa = saPrev;
n = nPrev;
return res;
} else {
return function (){
// arguments are params, so closure bussiness is avoided.
return accumulator(arguments,sa.slice(0),n);
}
}
}
return accumulator([],sa,n);
}

This code shows the basic structure of the problem; return a function that builds arguments until it can call the target. The author continues with an admission that this has some imperfections, but they don’t talk about how it reads. Maybe I shouldn’t put too much stock in currying of all things, but I think that the readability of something so fundamental is telling of the core language features.

Now, if you are familiar with code golfing then you may think me hypocritical for judging code on readability, especially since I just praised JavaScript for having the ability to be written in only six characters. Golfing is reduction to the absurd, but with dedication and through distilling the necessary components one can learn more about a language and in particular how to better implement that idea. In that vein it was time to play golf.

ECMA5 Solution

function a(b,c,d){return(d=d||[]).length<b.length?function(){return a(b,c,d.concat.apply(d,arguments))}:b.apply(c,d)}

This solution takes a target, scope, and any number of arguments in an array. Once called with a target (scope and arguments are optional) it returns an anonymous function, tasked with concatenating the Arguments object with those already provided and calling back to the outer function. One could also remove the scope argument, however Function.prototype.apply would still be necessary as the arguments are packed into an array.

I was satisfied at the time but even then I didn’t like the use of apply and the arguments object, they used a lot of bytes and don’t speak to the essence of currying, rather they read to me like an abuse of the language. For these reasons I found myself returning to this months later with ECMA6 in mind.

ECMA6 Solution

a=(b,...c)=>c.length<b.length&&((...d)=>a(b,...c,...d))||b(...c)

Writing this solution I had it in my mind that I was a machine searching for the fewest bytes, so while I would like to take credit it’s ECMA6 that made this beautiful. The spread operator and rest parameters made this all too easy, the symmetry of the two along with the compact arrow functions made this a joy to write. This makes the it only 64 bytes long using just 18 different characters.

Breakdown

I’m going to break this down for anyone who isn’t clear on how it works as it’s worth understanding.

a = (b, ...c) =>

To begin with I’m naming the function as ‘a’ to reuse it later, I then have the target ‘b’, and rest parameter ‘c’. The ellipses here indicates that all additional arguments will be packed into an array that can be referred to as ‘c’. Thankfully if there are no additional arguments ‘c’ will be an empty array thus eliminating the need to check if ‘c’ is defined.

Arrow functions that only have one statement do not need braces and return the result of that statement, meaning we can eliminate the nine bytes that would use.

c.length < b.length && … || …

After checking if we have enough arguments by comparing the size of the array with what the target wants, we return either our inner anonymous function or the result of the target. This is based on the equivalent of a ternary operator. A ternary operator is avoided as it would add bytes by making this more than one statement.

((...d) => a(b, ...c, ...d))

Here is the greatest concentration of ECMA6 magic, our inner function is now so simple and has both self symmetry and outer; this to me is beautiful code. After taking any number of new arguments into a new rest parameter ‘d’, we call back to ‘a’ and pass it the target along with all the arguments we have so far. Ellipses makes an appearance again now as the spread operator to unpack the arrays. The whole is compounded by the tiny footprint of the one statement arrow function. Much better than Array.prototype.concat.apply don’t you think?

b(...c)

As you may expect we can now use the spread operator when calling the target and avoid using apply once more.

Conclusion

In general golfing tends towards chaotic and unreadable code, but in it’s own way it can also achieve beauty that informs of how to better write code. I advise against using golfed code in production as apart from being hard to read and maintain; even if the function works perfectly it can also be hard to step through in debugging. As such here is the function in a more readable form along with a bonus ECMA7 method decorator that makes methods curryable.

function curry(f, s, ...args) {
if (f.length <= args.length)
return f.call(s, ...args);
  return (...more) => curry(f, s, ...args, ...more);
}
function curryable(target, name, descriptor) {
let fn = descriptor.value;
  descriptor.value = function(...args) {
return curry(fn, this, ...args);
};
}
class Example {
constructor() {
this.sum = 0;
}
  @curryable
test(a, b, c) {
this.sum = a + b + c;
return this.sum;
}
}

You can find this example running here.

This pattern will allow you to make any method that takes any number of arguments curryable (be careful with defaults and rest parameters) in a generic way. The only lacking part of this is that currying in this way can not achieve partial compilation or computation. I believe a more invasive pattern is necessary to achieve that feature of currying, however perhaps with some generator magic it can be simplified.

Lies

You may have noticed I sacrificed some functionality in writing the ECMA6 solution as it no longer has a scope argument, thus making it harder to call methods. You can write this with a scope argument but I couldn’t resist showing off the function without. The scope argument is not always necessary and it reintroduces the second apply adding nine bytes. Lastly the callback ‘a’ leaks to global scope, so some modification of the solution depending on context is necessary before use.