Functional Programming in JavaScript using LiveScript and prelude.ls

19 Jun 2012, updated 30 Jul 2012 - George Zahariev

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.

Are you familiar with CoffeeScript? If so you can skip ahead a couple of sections.

But first, we'll briefly go over the basics of LiveScript:

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.

Things become a quite a bit lighter in LiveScript:


(x, y) -> x + y 

-> # an empty function 

times = (x, y) ->
  x * y
# multiple lines, and be assigned to a var like in JavaScript
var times;
(function(x, y){ return x + y; });

(function(){});

times = function(x, y){
  return x * y;
};

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.


addTwo = (x) -> x + 2

wrap = (f, x) ->      # take a function as an argument
  (y) -> x + f(y) + x # returns a function

dollarAddTwo = wrap addTwo, '$'
dollarAddTwo 3        #=> '$5$'
var addTwo, wrap, dollarAddTwo;
addTwo = function(x){ return x + 2; };

wrap = function(f, x){
  return function(y){ return x + f(y) + x; };
};
dollarAddTwo = wrap(addTwo, '$');
dollarAddTwo(3);

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:


times = (x, y) --> x * y
times 2, 3          #=> 6 (normal use works as expected)
double = times 2
double 5            #=> 10
var times, double;
times = curry$(function(x, y){ return x * y; });
times(2, 3);
double = times(2);
double(5);
function curry$(f, args){
  return f.length > 1 ? function(){
    var params = args ? args.concat() : [];
    return params.push.apply(params, arguments) < f.length && arguments.length ?
      curry$.call(this, f, params) : f.apply(this, params);
  } : f;
}

Currying allows you to be concise in your higher order collection functions. Say we didn't have double defined.

map (times 2), [1, 2, 3]  #=> [2, 4, 6]
map(times(2), [1, 2, 3]);

Currying makes it really easy to define functions as a subset of the behavior of one of your already defined functions. But what if you want to combine the behavior of multiple functions together?

Composing Functions

Composing allows you to create functions by 'composing' them out of a series of functions. LiveScript has two operators for composing, forward >> and backwards <<.

(f << g) x is equivalent to f(g(x)), and (f >> g) x is equivalent to g(f(x)). For example:


even    = (x) -> x % 2 == 0
invert  = (x) -> not x
odd     = invert << even
var even, invert, odd;
even = function(x){ return x % 2 === 0; };
invert = function(x){ return !x; };
odd = compose$([invert, even]);
function compose$(fs){
  return function(){
    var i, args = arguments;
    for (i = fs.length; i > 0; --i) { args = [fs[i-1].apply(this, args)]; }
    return args[0];
  };
}

Say odd wasn't defined, how could we easily filter a list without having to define a new function?

filter (invert << even), [1, 2, 3, 4, 5] #=> [1, 3, 5]
filter(compose$([invert, even]), [1, 2, 3, 4, 5]);
function compose$(fs){
  return function(){
    var i, args = arguments;
    for (i = fs.length; i > 0; --i) { args = [fs[i-1].apply(this, args)]; }
    return args[0];
  };
}

Yes F# users, it's just like F#! LiveScript also includes your beloved pipe operator - more about that later.

Haskell programmers, you can use a spaced dot instead of <<, for example f . g.

Having to define functions for really basic operations seems tedious, is there a better way?

Operators as Functions

You can use operators as functions, simply by wrapping them in parentheses. You can also partially apply their arguments!


timesTwo = (* 2)
timesTwo 4 #=> 8

odd = (not) << even
var timesTwo, odd;
timesTwo = (function(it){ return it * 2; });
timesTwo(4);

odd = compose$([not$, even]);
function compose$(fs){
  return function(){
    var i, args = arguments;
    for (i = fs.length; i > 0; --i) { args = [fs[i-1].apply(this, args)]; }
    return args[0];
  };
}
function not$(x){ return !x; }

All this is great, but what if you have several steps in your process?

Piping

Instead of a series of nested function calls, you can pipe values in:

[1, 2, 3, 4, 5] |> filter even |> map (* 2) |> fold (+), 0 
#=> 12
fold(curry$(function(x$, y$){
  return x$ + y$;
}), 0)(
map((function(it){
  return it * 2;
}))(
filter(even)(
[1, 2, 3, 4, 5])));
function curry$(f, args){
  return f.length > 1 ? function(){
    var params = args ? args.concat() : [];
    return params.push.apply(params, arguments) < f.length && arguments.length ?
      curry$.call(this, f, params) : f.apply(this, params);
  } : f;
}

But if the functionality you want can't be created through partial application of a curried function, composing a function, or the use of an operator?

Implicit Argument

If the function you are defining only has one argument, then you can omit the (x) -> part and just use it as an implicit argument.

filter (-> it.length > 5), ['tiny', 'middle', 'longer'] 
#=> ['middle', 'longer']
filter(function(it){
  return it.length > 5;
}, ['tiny', 'middle', 'longer']);

Infix Functions

You can use functions like operators, and even partially apply their second argument if desired - just wrap them in backticks.


startsWith = (xs, x) -> x == xs.slice 0, 1
'hello' `startsWith` 'h' #=> true
var startsWith;
startsWith = function(xs, x){ return x === xs.slice(0, 1); };
startsWith('hello', 'h');

Comprehensions

Tired of seeing maps and filters? Or want to process several collections at a time? You can use list comprehensions and object comprehensions.




r = [x + 1 for x in [2, 3, 4, 5] when x + 1 isnt 4] 
r #=> [3, 5, 6] 








r = {[key, val * 2] for key, val of {a: 1, b: 2}}   
r #=> {a: 2, b: 4}








r = [x + y for x in [1, 2] for y in ['a', 'b']]   
r #=> ['1a', '1b', '2a', '2b']
var res$, i$, ref$, len$, x, r, key, val, j$, ref1$, len1$, y;
res$ = [];
for (i$ = 0, len$ = (ref$ = [2, 3, 4, 5]).length; i$ < len$; ++i$) {
  x = ref$[i$];
  if (x + 1 !== 4) {
    res$.push(x + 1);
  }
}
r = res$;
r;
res$ = {};
for (key in ref$ = {
  a: 1,
  b: 2
}) {
  val = ref$[key];
  res$[key] = val * 2;
}
r = res$;
r;
res$ = [];
for (i$ = 0, len$ = (ref$ = [1, 2]).length; i$ < len$; ++i$) {
  x = ref$[i$];
  for (j$ = 0, len1$ = (ref1$ = ['a', 'b']).length; j$ < len1$; ++j$) {
    y = ref1$[j$];
    res$.push(x + y);
  }
}
r = res$;
r;

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:

take = (n, [x, ...xs]:list) -->
  | n <= 0     => []
  | empty list => []
  | otherwise  => [x] +++ (take n - 1, xs)

take 2, [1 to 5] #=> [1, 2]

Check out their sites for more: LiveScript and prelude.ls.

Follow the discussion on Hacker News, r/programming, and r/javascript.

Read Functional Programming in JavaScript using LiveScript - Part 2.


For more on LiveScript and prelude.ls, .

comments powered by Disqus