2016 / 07 / 06

Functional Programming & Elixir – Pt. 1, The Basics

It might be difficult to get into functional programming because of the complexity and the terminology. The goal of these articles is to explain the terminology in a simple manner, reduce the complexity and at the same time, give code samples written in Elixir and show how Elixir helps us with functional programming. This first article is all about functions, variables and patterns.


First-class functions

This doesn't say much, but it basically just means that functions can be stored in variables, that you can pass them to other functions and invoke them there. In Elixir these are called anonymous functions.

some_function = fn(arg) -> do_whatever end

You also have named functions.

defmodule Do do
  def something do

These cannot be passed down to other functions. But you can convert them to anonymous functions.

some_function.( &Do.something/0 )

/0 is the amount of arguments the function accepts, ie. the arity

There's also another syntax which you'll see sometimes, for example:

Enum.reduce(["hello", " ", "world"], &(&2 <> &1))

Returns "hello world"

So what is this &(&2 <> &1) thing? It's a concise anonymous function, a so-called partially applied function. Where &1 is the first argument of the function and &2 the second.

To fully explain the example, <> concats the two strings (ie. the two function arguments). Check the Elixir docs for more info about the Enum.reduce function.


Composing, function composition, is essentially executing a sequence of functions. That is, the result of each function is passed as the argument of the next.

add_one = fn(integer) ->
  integer + 1

multiply_by_four = fn(integer) ->
  integer * 4

# execute functions
multiply_by_four.(add_one.(1))             # returns '8'
multiply_by_four.(add_one.(add_one.(2)))   # returns '16'

Elixir provides a way to make it more clear what we're trying to do here. This operator is called the pipe operator.

# This is easier to read, especially for mathematical operations.
# ((2 + 1) + 1) * 4
2 |> add_one.() |> add_one.() |> multiply_by_four.()

This is the syntax for anonymous functions, which looks a bit odd, if I were using named functions I could omit the trailing .()


A closure is a function which has the following properties:

  • Is a first-class function.
  • Remembers the values of all the variables in scope when the function was created.
x = 2

f_x = fn ->
  y = :math.pow(x, 2)

f_x.() # returns 4

x = 3

f_x.() # still returns 4

:math is a Erlang module, which in Elixir are represented by atoms. Elixir runs on top of the Erlang VM, which makes it possible to use Erlang code with Elixir

Higher-order functions

A higher-order function (HoF) is a function which takes one or more functions as arguments and/or a function that returns a new function, which is a closure.


in_p_tags = &("<p>" <> &1 <> "</p>")
in_nav_tags = &("<nav>" <> &1 <> "</nav>")
in_body_tags = &("<body>" <> &1 <> "</body>")

wrap = fn(wrapping_functions) ->
  fn(html) ->
    reducer = fn(function, acc) -> function.(acc) end
    Enum.reduce(wrapping_functions, html, reducer)

wrap.([in_p_tags, in_body_tags]).("Text")

# result:
# <body><p>Text</p></body>

wrap.([in_nav_tags, in_body_tags]).("<a href=\"...\">Link</a>")

# result:
# <body><nav><a href="...">Link</a></nav></body>

This example sums it up, we have:

  • First-class functions
  • Function composition in the form of a reducer
  • A closure, the function returned by the wrap function
  • A higher-order function named wrap

Pattern matching & Immutability

In Elixir the = operator doesn't only do assignment, but also pattern matching. That's why it's called the match operator. This also means that variables aren't really places in memory that values are stored in, but rather labels for the values. That is, Elixir/Erlang allocates memory for the values, not the variables.

# attached the value '1' to the label 'nr'
nr = 1

# rebind the 'nr' label to the value '2'
nr = 2

# let's see why this is called the match operator
# the following is a valid expression,
# because of the previous expression
2 = nr

# this is not, because 3 does not match 2,
# this will raise a MatchError
3 = nr

We can also do destructuring:

# 1. destructure by using tuples
{ a, b } = { :ok, "Hi" }


# this won't work,
# we need 3 variable names on the left side
{ a, b } = { :ok, "Hi", "extra-extra" }

# this will work
{ a, b, type } = { :ok, "Hi", "extra-extra" }
{ a, b, _ } = { :ok, "Hi", "extra-extra" }

# 2. destructure by using lists
[ a, b ] = [ "first", "second" ]

_ is used to ignore a value

Why this is useful

Say you have a function that returns a tuple, but you don't want to do this all the time:

result = some_function()
state = elem(result, 0)
message = elem(result, 1)

elem gets a specific item out of the tuple.

Wouldn't it be way easier if we could just write:

{ state, message } = some_function()

Yes, it is.


With that in mind, in Elixir, all data types are immutable.

tuple = { :number, 1 }

# if we would change the tuple, we would get a new one
# for example, change the second value in the tuple:
put_elem(tuple, 1, 1000)

# returns new tuple: { :number, 1000 }
# tuple variable still points to the tuple: { :number, 1 }

Why this is useful

Imagine if we didn't have immutable date types.

state = %{ example: "Property" }

func_a = fn(s) ->
  # remove 'example' property from the state map
  Map.delete(s, :example)

func_b = fn(s) ->
  str = s.example <> ": example"


%{} is a key-value store, a map

In this case func_b would break, because it depends on the example property and we deleted that property in func_a.

If we apply immutability, for example, Map.delete produces an error, or as in Elixir, Map.delete returns a new map and doesn't touch the original map, func_b will work fine.


I think we have discussed all the basics of functional programming and Elixir. Except for some basic Elixir types, but you can look those up easily. Elixir has some pretty good getting-started guides.

Next up in the functional programming category is what to do and what not do with functions. Or to put in technical terms, limiting side-effects, data-first and using map and reduce instead of a loop.

Credits: Thanks to Brooklyn Zelenka and Izaak Schroeder for all the help!