Functions in Elixir

Published September 26, 2018 by Toran Billups

In part 3 of my Elixir journey I'll be sharing some about functions.

Functions

A simple function is defined using the `fn` keyword. Directly following you take the arguments followed by the -> symbol. Next the function body followed by the `end`

    total = fn (a, b) -> a + b end
  

You would then invoke the function like this

    total.(1, 2)
  

Even if the function doesn't take any parameters, you are still required to invoke it using parentheses.

    word = fn -> IO.puts "hello" end
    word.()
  

You can omit parentheses in the function definition (optional). I've personally found this less explicit but you might prefer it.

    total = fn a, b -> a + b end
    total.(3, 4)
  

It's important to note that when we pass parameters to a function Elixir is doing a match, not assignment like we might think traditionally.

    total = fn (a, b) -> a + b end
    total.(1, 2)
    {a, b} = {1, 2}
  

A single function definition may have many different implementations. The order that these functions appear is important. Working top down the first that has a match for the arguments you are passing will win.

    defmodule Foo do
      def my_func({name}) do IO.puts("#{name}") end
      def my_func({name, number}) do IO.puts("#{name} #{number}") end
    end
  

You can follow along using IEx by throwing this script into a file called `foo.exs` and running the command below.

    iex -S mix run foo.exs
  

IEx provides an interactive session that you can use to learn the language. To invoke the first function above pass only the name parameter.

    Foo.my_func({"toran"})
  

To invoke the 2nd implementation pass both name and number.

    Foo.my_func({"toran", 123})
  

Functions can return other functions. In the example below we first bind the `word` variable to the function returned.

    word = fn -> fn -> "hello" end end
  

We can invoke this returned function to see the output "hello". Note: if we only invoke the first function we won't see the value "hello" so instead we must invoke it twice.

    word.().()
  

To see the power of functions that return functions we should create a function for `add` that allows us to create new functions and call those with some value. In the example below we create a function that explicitly adds 3 to whatever you pass it.

    add = fn (a) -> fn (b) -> a + b end end
  

To create the first function we only need to invoke the first function

    add_three = add.(3)
  

Next we can use the add_three function to add any other number we pass

    add_three.(4)
    add_three.(5)
    add_three.(6)
  

One trick to make the inner most function body more clear is to wrap it with parentheses

    add = fn (a) -> (fn (b) -> a + b end) end
  

In the add examples above the `a` variable is bound in the scope of the outer function. But when the inner most function is defined, it will inherit this same scope... meaning we have just seen our first closure in action! You might have heard this referred to as lexical scoping. To learn more checkout the Elixir language documentation.

Because functions are just values in Elixir, we can pass them to other functions.

    add_one = fn (a) -> a + 1 end
    apply = fn (fun, value) -> fun.(value) end
    apply.(add_one, 9)
  

In the example above we create a function `add_one` and then pass it to another function `apply` that invokes it and returns the value 10

Below is another example of this behavior using the Enum module to transform a list of numbers.

    list = [1, 2, 3]
    Enum.map(list, fn (e) -> e + 1 end)
  

This will enumerate the list and return a new list with values [2, 3, 4]


Buy Me a Coffee

Twitter / Github / Email