Functions, Functions and more Functions
Posted on November 15, 2014 by Clive in Elixir
Elixir is a functional programming language, and as such you should expect that it has functions. In fact, like many functional and imperative languages it has two sorts: anonymous and named.
Before I go on and discuss functions, I need to cover the concept of arity. The arity of a function is the number of arguments that it takes. This is important in Elixir (and Erlang) because functions with the same name, but different aritys are different functions and have a different type. This means that: are all different. As you can see the arity of a function is denoted by /x after the function name. The arity of the funciton helps Elixir use the right thing. Anyway back to the billed discussion. As you might have expected for a functional programming language, functions are first-class citizens and anonymous functions are no exception. In Elixir the syntax for anonymous functions is as follows: because of pattern-matching, the function can take more than one set of parameter lists to provide alternate bodies. As you can see to call an anonymous function dot-notation is used. From the example as well you can see that the function body executed is determined by the pattern of the parameter list. This is a useful concept to understand when we move to move advanced topics. After the function definition iex prints out the internal reference to that function. As functions are first-class citizens in Elixir, functions are a valid return type for functions, meaning that functions can return functions (that can return functions ad infinitum). This is demonstrated by the following rather contrived example: This syntax for anonymous functions is all well and good, but it can look a little bit verbose when required for quick data transformations or small helper functions in operations like Enum.map/2. Luckily Elixir provides us with some syntactic sugar to make these things more readable. &(): which is equivalent to: It is of course a matter of opinion, but for me the first shorter form is more readable. In this alternative syntax, &n is an parameter list index, so &1 means the first parameter, &2 the second and so on: A named function is just that: a function with a name. In Elixir named functions reside within modules and a module lives in a file. There are two types of file, script file (with .exs extension) and code file (with .ex extension). The difference is that files with the .ex extension are intended for compilation with elixirc, whilst .exs are intended for use with elixir directly. To demonstrate this, put this file in a location of your choosing on your file system: MyMath.ex
In this file put the following: As you can see this is just a simple set of basic math operations, nothing too complicated about that. What you might also notice is that divide is defined twice, once for the case where the denominator is zero and then for all other cases. This is a typical pattern, which makes use of pattern-matching. To use this, from the directory where you put this file, start an iex session: The first thing that we’ll do here is use the iex c() function to compile and include this module. we can then start using the functions contained within this module in iex: so here you can see each of the operations defined in the file being used. As you can see, to be able to call the function you need to call it using the module name first. This namespacing is necessary when calling a function in this manner. If you are calling a function from within the same module then it is not necessary to use the module name, as demonstrated in the following listing for MyMath2.ex: I’m also demonstrating here two other features that Elixir has: alternative function definition syntax and the capture operator (&). So to recap Elixir named function definition: For a typical example of recursion - factorial.ex:func(a, b) # func/2
func(a, b, c) # func/3
func(a, b, c, d) # func/4
Anonymous Functions
fn
parameter_list -> function_body
parameter_list -> function_body
end
iex(1)> checker = fn
...(1)> {:ok, a} -> a
...(1)> {:error, a} -> "error"
...(1)> end
#Function<6.90072148/1 in :erl_eval.expr/5>
iex(2)> checker.({:ok, 1})
1
iex(3)> checker.({:error, 1})
"error"
iex(1)> fun = fn -> (fn name -> "Hello #{name}" end ) end
#Function<20.90072148/0 in :erl_eval.expr/5>
iex(2)> hello = fun.()
#Function<6.90072148/1 in :erl_eval.expr/5>
iex(3)> hello.("World")
"Hello World"
iex(1)> Enum.map([1,2,3,4,5,6], &(&1 * &1))
[1, 4, 9, 16, 25, 36]
iex(2)>> Enum.map([1,2,3,4,5,6], fn a -> a * a end)
[1, 4, 9, 16, 25, 36]
iex(3)> add = &(&1 + &2)
&:erlang.+/2
iex(4)> add.(1,2)
3
...
iex(6)> subtract = &(&1 - &2)
&:erlang.-/2
iex(7)> subtract.(2,3)
-1
iex(8)> subtract = &(&2 - &1)
#Function<12.90072148/2 in :erl_eval.expr/5>
iex(9)> subtract.(2,3)
1
Named Functions
defmodule MyMath do
def plus(a, b) do
a + b
end
def multiply(a, b) do
a * b
end
def minus(a, b) do
a - b
end
def divide(_, b) when b == 0 do
raise "Cannot divide by zero"
end
def divide(a, b) do
a / b
end
end
$ iex
Erlang/OTP 17 [erts-6.2] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false]
Interactive Elixir (1.1.0-dev) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)>
iex(1)> c("MyMath.ex")
[MyMath]
iex(2)>
iex(2)> MyMath.plus(1, 2)
3
iex(3)> MyMath.multiply(3, 9)
27
iex(4)> MyMath.minus(200, 300)
-100
iex(5)> MyMath.divide(3, 4)
0.75
iex(6)> MyMath.divide(300, 0)
** (RuntimeError) Cannot divide by zero
MyMath.ex:15: MyMath.divide/2
defmodule MyMath2 do
def plus(a, b), do: operation(a, b, &+/2)
def minus(a, b), do: operation(a, b, &-/2)
def multiply(a, b), do: operation(a, b, &*/2)
def divide(_, 0), do: raise "Divide by zero error"
def divide(a, b), do: operation(a, b, &//2)
defp operation(a, b, func), do: func.(a, b)
end
defmodule ModuleName do
def function(arg1, arg2), do: functionBody
def function(arg1, arg2), do: (
functionBody
)
def function(arg1, arg2) do
functionBody
end
end
defmodule Factorial do
def of(0), do: 1
def of(n), do n * of(n-1)
end