Erlang and Elixir, Part 3: Functions

Elixir is built on Erlang, and in both Erlang and Elixir a function is not just identified by its name, but by its name and arity.  Remember: Everything in Elixir is an expression.

Erlang and Elixir, Part 3: Functions

To give you a clear example of this, below we have four functions, but all defined with different arity.

def sum, do: 0
def sum(a), do: a
def sum(a, b), do: a + b
def sum(a, b, c), do: a + b + c

To provide a concise way to work with data, we can use guard expressions like so:

def sum(a, b) when is_integer(a) and is_integer(b) do
  a + b

def sum(a, b) when is_list(a) and is_list(b) do
  a ++ b

def sum(a, b) when is_binary(a) and is_binary(b) do
  a <> b

sum 1, 2
#=> 3

sum [1], [2]
#=> [1, 2]

sum "a", "b"
#=> "ab"

Guard expressions such as when is_binary(a) allow us to check for the correct type before performing an operation.

Unlike Erlang, Elixir allows for default values in its functions via usage of the \ syntax like so:

def mul_by(x, n \ 2) do
  x * n

mul_by 4, 3 #=> 12
mul_by 4    #=> 8

Anonymous Functions

In the previous part, we discussed anonymous functions briefly as a data type. To elaborate on this further, take this example:

sum = fn(a, b) -> a + b end
sum.(4, 3)
#=> 7

square = fn(x) -> x * x end [1, 2, 3, 4], square
#=> [1, 4, 9, 16]

We see this powerful shorthand here on the first line fn(a, b) -> a + b end. With this we are able to produce a basic operation sum.(4, 3) and get the output in just two lines of code.

Now, looking to the square method, fn(x) -> x * x end, can it really be any simpler? Working now with the map, we can perform the square anonymous function over the whole map—again in just two lines of code!

Pattern Matching

Arithmetic is all fun and good, but let’s see what we can do with text.

f = fn
      {:simon} = tuple ->
        IO.puts "Good morning Sir #{inspect tuple}"
      {:kate} = tuple ->
        IO.puts "Lovely day #{inspect tuple}"
      [] ->

#=> "Empty"

#=> "Good morning Sir {:simon}"

#=> "Lovely day {:kate}"

Here, with pattern matching we can define several outcomes in our control flow, again in hardly any code. Elixir’s syntax is rapid to work with and mightily powerful, as we will see in the next example.

First-Class Functions

The anonymous functions we just covered are first-class values. This means that they can be passed as arguments to other functions and also can serve as a return value themselves. There is a special syntax to work with named functions in the same way:

defmodule Math do
  def square(x) do
    x * x
end [1, 2, 3], &Math.square/1
#=> [1, 4, 9]

Here we define a Math module with defmodule and define the square function. Then we can use this in conjunction with the map method demonstrated earlier and the Math module we just defined. We use the same operator &, allowing us to pass our function Math.square/1 to capture the square function’s output for each entry in our list.

That’s a whole lot of power for just one line. This is referred to as a partial function capture in Elixir.

Control Flow

We use the constructs if and case to control flow in Elixir. Like everything in Elixir, if and case are expressions.

For pattern matching, we use case:

case {x, y} do
  {:a, :b} -> :ok
  {:b, :c} -> :good
  other -> other

And for comparison logic, we use if:

test_fun = fn(x) ->
  cond do
    x > 10 ->
    x < 10 and x > 0 ->
    x < 0 or x === 0 ->
    true ->

#=> :greater_than_ten

#=> :zero_or_negative

#=> :exactly_ten

For ease of use, Elixir also provides an if function that resembles many other languages and is useful when you need to check if one clause is true or false:

if x > 10 do

Dynamic Functions

This is possible in Elixir via usage of the unquote method mentioned earlier. For example, to check some hard-coded Admins in our system, we can do the following:

defmodule Admins do
  [:manager, :super] |> Enum.each fn level ->
    def unquote(:"check_#{level}")() do
      IO.inspect("Access Level: #{unquote(level)}")

Admins.check_manager  # => "Access Level: manager"
Admins.check_super    # => "Access Level: super"

Here we have created the methods Admins.check_manager and Admins.check_super from the atom names.


Everything is an expression in Elixir, and that means we can get a whole heap of power out of writing very little code.

For me, Elixir looks similar to CoffeeScript, Ruby, Python or any minimalist syntax as the form is so direct and to the point, but Elixir is far more powerful than these languages due to the meta-programming aspect.

Going forward, we will see how to utilise control flow and functions more to create interactivity in our app.