From JavaScript to Elixir

I like functional programming, especially in JavaScript because it is so expressive. On my journey to learn Elixir, I quickly realized I need to understand recursion. In Elixir, you don’t have any for-loops! Some of the most common functions when using functional programming are reduce, map and filter. Let’s see how we can start with a javascript implementation (also recursive) and implement the same functionality using Elixir.

Reduce

The core function in this tutorial is reduce, because all the other functions can be derived from it. Here we have reduce implemented in javascript using recursion, taken from a talk by Brian Lonsdorf aka DrBoolean. Check it out, it is awesome.

const head = list => list[0];
const tail = list => list.slice(1, list.length);

const reduce = (fun, acc, list) => {
  if (list.length === 0) return acc;
  return reduce(fun, fun(acc, head(list)), tail(list));
}

How do we implement this in Elixir? Let’s start with a ExUnit test!

test "reduce empty list" do
  square = fn a -> a * a end
  assert Fun.reduce(square, 0, []) == 0
end

Here we also note that anonymous functions are quite similar in javascript and elixir:

// square function in JavaScript
const square = a => a * a
# square function in Elixir
square = fn a -> a * a end

And now we implement the case for our empty list:

defmodule Fun do
  def reduce(_, acc, []) do
    acc
  end
end

We pattern match agains an empty list, and just return our accumulator. We use _ to prevent the compiler yelling at us about unused variables.

And now, lets make it more interesting, by implementing the other case. Let’s add another test.

test "reduce simple list" do
  sum = fn (acc, val) -> acc + val end
  assert Fun.reduce(sum, 0, [1,2,3,4]) == 10
end

Now lets make the test pass by implementing it.

defmodule Fun do
  def reduce(fun, acc, [head | tail]) do
    reduce(fun, fun.(acc, head), tail)
  end
  
  # ...
end

Here we are using pattern matching to extract head and tail of our list, similarly to the javascript version. Since we are recursing by calling ourself, we have the other function clause defined earlier to break the recursion when we reach an empty list. Note, when calling anonymous functions, you must have a dot between the function name and the parens: fun.(acc, head).

Map

Another common function in functional programming is map. It allows you to transform data, by applying a function to each item in a list, and return a new list with the resulting values.

Map can be implemented using reduce. You start with an empty list, and concat the result of the function for each item in the original list.

const map = (f, list) => {
  return reduce((acc, x) => acc.concat(f(x)), [], list);
}

Let’s see how we can implement it using Elixir. Let’s start with a unit test for our base case, an empty list.

test "map empty list" do
  square = fn x -> x * x end
  assert Fun.map(square, []) == []
end

And here is the implementation. We use _ to prevent the compiler yelling at us about unused variables.

defmodule Fun do
  def map(_, []) do
    []
  end

  # ..
end

Now, lets make it interesting by adding another test case.

test "map simple list" do
  square = fn x -> x * x end
  assert Fun.map(square, [1, 2, 3, 4]) == [1, 4, 9, 16]
end

Like in the javascript implementation, we have an anonymous reducer function that concats the accumulator with the result (using ++) of calling the supplied function. Before concating, we put the result in a new list.

defmodule Fun do
  def map(fun, list) do
    reducer = fn (acc, x) -> acc ++ [fun.(x)] end
    reduce(reducer, [], list)
  end
  
  # ..
end

And again, the recursion stops when the list becomes empty, and our first function clause gets called.

Filter

The last function in this tutorial is about filter. We pass a predicate function to determinate if the item should be part of the result or not. Again, we are using the reduce function to implement this functionality. It is quite interesting how useful reduce is! :)

const filter = (predicate, list) => {
  return reduce((acc, x) => predicate(x) ? acc.concat(x) : acc, [], list);
}

Let’s implement it in Elixir, and as usual we start with a test case. We want to filter out odd and even numbers.

test "filter simple list" do
  isEven = fn x -> rem(x, 2) == 0 end
  isOdd  = fn x -> rem(x, 2) != 0 end
  assert Fun.filter(isEven, [1, 2, 3, 4, 5]) == [2, 4]
  assert Fun.filter(isOdd,  [1, 2, 3, 4, 5]) == [1, 3, 5]
end

We implement our filter function. Then we create a reducer function that we can pass to reduce. If the supplied function returns true, we concat the value to our accumulator, else we just return the accumulator.

defmodule Fun do
  def filter(fun, list) do
    reducer = fn (acc, x) -> 
      if fun.(x) do
        acc ++ [x]
      else
        acc
      end
    end
    reduce(reducer, [], list)
  end

  # ...
end

There you have it. I hope you have learned something, I sure did. Thanks for reading!