Functional refactoring with LiveScript

sibelius_pipes
Original image by Dennis Jarvis. Used under Creative Commons BY-SA license.

LiveScript is a functional language that compiles to JavaScript. You could say it's sort of like CoffeeScript, but in fact it's so much better. This post features one hands on example of refactoring Javascript-like code using power of LiveScript syntax, combined with Prelude.ls extras.

Here is a function for processing an array of tags into an object for D3.js vizualisation component. On input you have an array like ['tag1', 'tag2', 'tag2', 'tag2', 'tag3', ... ]. The function selects the top 10 of the most popular tags and constructs a D3.js compatible object.

However when I showed this code to Viktor, he was quick to point out that LiveScript can do better. After another couple of minutes he produced this:

8 LOC vs. 21 LOC. The beauty of LiveScript is that it is very readable, and you can figure out what's going on just by reading the code. Refactored version also compiles into a neat(er) looking JS.

What's going on here?

|> is a LiveScript directive for piping. You get results from a previous operation and pass it on to the next one. We are effectively processing a single input, so it is piping all the way.

group-by (-> it) -- using a Prelude.ls function to create index of the tags array. This will create an object which will look like this: {'tag1': ['tag1'], 'tag2': ['tag2', 'tag2', 'tag2'], ...} We can see a nice example of LiveScript syntax where -> it effectively compiles into:

Note that tags are piped into this function.

obj-to-pairs -- another Prelude.ls function, which takes an object and returns a list of pairs. This way our previous array will turn into something like this:

[['tag1', ['tag1']], ['tag2', ['tag2', 'tag2', 'tag2']], ... ]

map (-> [it[0], it[1].length]) -- maps every entry in the array using a supplied function. This will produce a new array:

[['tag1', 1], ['tag2', 3], ...]

Again, using the default argument it here for every array entry from the previous array.

sort-by (.1) -- .1 is a clever use of LiveScript syntax to access second entry in a ['tag2', 3] array and sort the master array based on its value. The sort-by function is again provided by the awesome Prelude.ls. Interesting detail here is that (.1) actually compiles into a function:

This means that you can do things like sort-by last, array, which will sort an array of arrays by the last item of each inner array (last being a prelude function again).

reverse -- simply reverses the array in order to get top 10 of the most used tags with the next step, which is...

take 10 -- takes 10 first entries from an array. It is smart enough to take less entries if the array is not big enough.

And all this leads to the last step:

map (-> {name: it[0], size: it[1]}) -- creates a final array of objects with name and size values. Final array will look like this:

[{name: 'tag2', size: 3}, {name: 'tag8', size: 2}, {name: 'tag1', size: 1}, ...]

In LiveScript last result is automatically returned, so there is no need to explicitly return value.

LiveScript is a very powerful language with (mostly) human readable syntax. Combined with Prelude.ls library you can write less code which looks elegant and does so much more.

Alex Savin

Software engineer

More from Alex