Passing Functions as Arguments

Functions are just values, so we can pass them to other functions.

 iex>​ times_2 = ​fn​ n -> n * 2 ​end
 #Function<12.17052888 in :erl_eval.expr/5>
 iex>​ apply = ​fn​ (fun, value) -> fun.(value) ​end
 #Function<12.17052888 in :erl_eval.expr/5>
 iex>​ apply.(times_2, 6)
 12

Here, apply is a function that takes a second function and a value. It returns the result of invoking that second function with the value as an argument.

We use the ability to pass functions around pretty much everywhere in Elixir code. For example, the built-in Enum module has a function called map. It takes two arguments: a collection and a function. It returns a list that is the result of applying that function to each element of the collection.

 iex>​ list = [1, 3, 5, 7, 9]
 [1, 3, 5, 7, 9]
 iex>​ Enum.map list, ​fn​ elem -> elem * 2 ​end
 [2, 6, 10, 14, 18]
 iex>​ Enum.map list, ​fn​ elem -> elem * elem ​end
 [1, 9, 25, 49, 81]
 iex>​ Enum.map list, ​fn​ elem -> elem > 6 ​end
 [false, false, false, true, true]

Pinned Values and Function Parameters

When we originally looked at pattern matching, we saw that the pin operator (^) allowed us to use the current value of a variable in a pattern. You can use this with function parameters, too.

functions/pin.exs
 defmodule​ Greeter ​do
 def​ for(name, greeting) ​do
 fn
  (^name) -> ​"​​#{​greeting​}​​ ​​#{​name​}​​"
  (_) -> ​"​​I don't know you"
 end
 end
 end
 
 mr_valim = Greeter.for(​"​​José"​, ​"​​Oi!"​)
 
 IO.puts mr_valim.(​"​​José"​) ​# => Oi! José
 IO.puts mr_valim.(​"​​Dave"​) ​# => I don't know you

Here, the Greeter.for function returns a function with two heads. The first head matches when its first parameter is the value of the name passed to for.

The & Notation

The strategy of creating short helper functions is so common that Elixir provides a shortcut. Let’s look at it in use before we explore what’s going on.

 iex>​ add_one = &(&1 + 1) ​# same as add_one = fn (n) -> n + 1 end
 #Function<6.17052888 in :erl_eval.expr/5>
 iex>​ add_one.(44)
 45
 iex>​ square = &(&1 * &1)
 #Function<6.17052888 in :erl_eval.expr/5>
 iex>​ square.(8)
 64
 iex>​ speak = &(IO.puts(&1))
 &IO.puts/1
 iex>​ speak.(​"​​Hello"​)
 Hello
 :ok

The & operator converts the expression that follows into a function. Inside that expression, the placeholders &1, &2, and so on correspond to the first, second, and subsequent parameters of the function. So &(&1 + &2) will be converted to fn p1, p2 -> p1 + p2 end.

If you think that’s clever, take a look at the speak line in the previous code. Normally Elixir would have generated an anonymous function, so &(IO.puts(&1)) would become fn x -> IO.puts(x) end. But Elixir noticed that the body of the anonymous function was simply a call to a named function (the IO function puts) and that the parameters were in the correct order (that is, the first parameter to the anonymous function was the first parameter to the named function, and so on). So Elixir optimized away the anonymous function, replacing it with a direct reference to the function, IO.puts/1.

For this to work, the arguments must be in the correct order:

 iex>​ rnd = &(Float.round(&1, &2))
 &Float.round/2
 iex>​ rnd = &(Float.round(&2, &1))
 #Function<12.17052888 in :erl_eval.expr/5>

You might see references to Erlang pop up when you define functions this way. That’s because Elixir runs on the Erlang VM. There’s more evidence of this if you try something like &abs(&1). Here Elixir maps your use of the abs function directly into the underlying Erlang library, and returns &:erlang.abs/1.

Because [] and {} are operators in Elixir, literal lists and tuples can also be turned into functions. Here’s a function that returns a tuple containing the quotient and remainder of dividing two integers:

 iex>​ divrem = &{ div(&1,&2), rem(&1,&2) }
 #Function<12.17052888 in :erl_eval.expr/5>
 iex>​ divrem.(13, 5)
 {2, 3}

Finally, the & capture operator works with string (and string-like) literals:

 iex>​ s = &​"​​bacon and ​​#{​&1​}​​"
 #Function<6.99386804/1 in :erl_eval.expr/5>
 iex>​ s.(​"​​custard"​)
 "bacon and custard"
 
 iex>​ match_end = &​~r/.*#{&1}$/
 #Function<6.99386804/1 in :erl_eval.expr/5>
 iex>​ ​"​​cat"​ =~ match_end.(​"​​t"​)
 true
 iex>​ ​"​​cat"​ =~ match_end.(​"​​!"​)
 false

There’s a second form of the & function capture operator. You can give it the name and arity (number of parameters) of an existing function, and it will return an anonymous function that calls it. The arguments you pass to the anonymous function will in turn be passed to the named function. We’ve already seen this: when we entered &(IO.puts(&1)) into iex, it displayed the result as &IO.puts/1. In this case, puts is a function in the IO module, and it takes one argument. The Elixir way of naming this is IO.puts/1. If we place an & in front of this, we wrap it in a function. Here are some other examples:

 iex>​ l = &length/1
 &:erlang.length/1
 iex>​ l.([1,3,5,7])
 4
 
 iex>​ len = &Enum.count/1
 &Enum.count/1
 iex>​ len.([1,2,3,4])
 4
 
 iex>​ m = &Kernel.min/2 ​# This is an alias for the Erlang function
 &:erlang.min/2
 iex>​ m.(99,88)
 88

This works with named functions we write, as well (but we haven’t covered how to write them yet).

The & shortcut gives us a wonderful way to pass functions to other functions.

 iex>​ Enum.map [1,2,3,4], &(&1 + 1)
 [2, 3, 4, 5]
 iex>​ Enum.map [1,2,3,4], &(&1 * &1)
 [1, 4, 9, 16]
 iex>​ Enum.map [1,2,3,4], &(&1 < 3)
 [true, true, false, false]