Functional Programming in JavaScript using LiveScript and prelude.ls

JavaScript is a great language, with powerful support for functional style programming. Unfortunately, its abilities are obscured by an awkward syntax and a lack of a good standard library.

LiveScript is a language that compiles to JavaScript. It's just JavaScript with some syntax improvements and feature additions, making functional style programming easier.

prelude.ls is a JavaScript library (written in LiveScript) which provides a rich set of functions to assist in functional style programming. It is somewhat based off of Haskell's Prelude module. It's the recommended base library for LiveScript, but will work with vanilla JavaScript as well.

What is meant by functional style programming? Well, we will be discussing functions as first class values, higher order functions, iteration, currying, composing, piping, operators as functions, and more.

A Brief Overview of LiveScript

Like many modern languages, blocks are delimited by whitespace indentation and newlines are used instead of semicolons to terminate statements (you can still use semicolons if you want to fit more than one statement on a line).

For example (LiveScript on the left, compiled JavaScript on the right):

if 2 + 2 == 4
doSomething()

if (2 + 2 === 4) {
doSomething();
}

You can try all these examples for yourself using the LiveScript compiler/repl on the LiveScript site.

To further clear things up, you can omit the parentheses when calling a function.

add 2, 3

add(2, 3);

And comments are:

# from here to the end of the line.

// from here to the end of the line.

Lisp hackers, you may be pleased to know that you can use dashes in the name of your variables and functions. The names are equivalent to, and are compiled to, camel case. Eg. my-value = 42 == myValue = 42.

Defining Functions

Defining functions, which we do very often in functional programming, can be cumbersome in JavaScript, as you have to use an eight letter keyword function, and have all the curly braces and semicolons.

As you see, function definitions are considerably shorter! You may also have noticed that we have omitted return. In LiveScript, almost everything is an expression and the last one reached is automatically returned. However, you can still use return to force returns if you want, and you can add a bang to your definitions to suppress auto-returning noRet = !(x) -> ....

First Class and Higher Order Functions

As in JavaScript, functions in LiveScript are first class values. That means they can be created at execution, stored in data structures, passed as arguments, and returned from functions. Functions which take a function as an argument are higher order functions.

Some of the most used higher order functions are those dealing with collections.

Iteration

Programming often involves dealing with collections of items, be it lists, objects, or strings. Functional programming allows you to deal with collections in a powerful way. Here we will use functions from prelude.ls.

First, say you want to take a collection and do something to each element - that would call for a map. Its first argument is the function which will be applied to each element. It returns a new collection of the type you inputed. Thus a list will return a new list, and an object will return a new object.

map ((x) -> x + 2), [1, 2, 3] #=> [3, 4, 5]

map(function(x){ return x + 2; }, [1, 2, 3]);

You may want to take all the items in the collection and get a single value out of them - that calls for a fold. The first argument is a function with two arguments, it uses this function to combine the list into a single value. The second argument is the value to start off with.

fold ((x, y) -> x + y), 0, [1, 2, 3] #=> 6

fold(function(x, y){ return x + y; }, 0, [1, 2, 3]);

Or you may want to filter items out - which calls for the aptly named filter. The first argument is a function which it applies to each item of the list. If that function evaluates to true, it adds the item to the resulting collection.

filter even, {a: 1, b: 2, c: 3, d: 4} #=> {b: 2, d: 4}

filter(even, { a: 1, b: 2, c: 3, d: 4 });

There are many more functions available, just check out the prelude.ls site for documentation on all of them.

For these higher order functions, it's very useful to have a method of concisely creating the functions you want to use. There are several options which we will explore. First, you can use curried functions to create functions that are subsets of ones you have already defined. You can use composition to create functions that are the combination of several other functions you have defined, and you can also use operators as functions.

Currying

Curried functions are very powerful. Essentially, when called with less arguments than defined with, they return a partially applied function. This means that it returns a function whose arguments are those which you didn't supply, with the values for what you did supply already bound. They are defined in LiveScript using the long arrow. Perhaps an example will make things more clear:

Concatenation

You can use the concat operator +++ to join two lists, or add something to the end of a list.

[1, 2] +++ [3, 4] #=> [1, 2, 3, 4]
[1, 2] +++ 3 #=> [1, 2, 3]

[1, 2].concat([3, 4]);
[1, 2].concat(3);

Note that it returns a new list, and doesn't modify the inputed ones.

Conclusion

This has been a brief overview of some of the features available in LiveScript and prelude.ls to assist in functional style programming. Both have many more powerful and useful features - to give you a taste: