Functions in Elixir
Sunday, 8th November, 2015
** defined functions
Functions are defined within a module.
Note that definition syntax and usage behaviour is slightly different depending on whether the function has a single argument or several arguments. For example, a space between function name and arguments is acceptable for a single-argument function, but is a syntax error with a multi-argument function:
def clown2a(x) do 5 * x end # ok def clown2b (x) do 5 * x end # ok def clown2b x do 5 * x end # ok def clown3a(x,y) do x + y end # ok def clown3b (x,y) do x + y end # syntax error def clown3c x,y do x + y end # ok
Similarly when using functions:
iex(1)> Drop.clown2a(5)
5
iex(2)> Drop.clown2a (5)
5
iex(3)> Drop.clown3a(5,6)
11
iex(4)> Drop.clown3a (5,6)
** (SyntaxError) iex:81: unexpected parentheses. If you are making
a function call, do not insert spaces between the function name
and the opening parentheses. Syntax error before: '('
There is also an optional short form of function definition which uses the construction “, do:” instead of “do” and omits “end”. This form can be used if the function body fits on one line, e.g.:
[7] def clown4b(x,y), do: x + y
[8] def clown4b(x,y), do:
x + y
Note that this short form is much stricter about spaces and brackets, but does still allow some creativity and individualism for the programmer:
def clown4d (x,y), do: x + y # syntax error - space after function name def clown4e x,y, do: x + y # syntax error - no brackets round arguments def clown4c(x,y), do:x + y # syntax error - no space after "do:" def clown4h(x,y) , do : x + y # syntax error - space between "do" and ":" def clown4f(x,y) , do: x + y # ok - space around "," def clown4g(x,y) ,do: x + y # ok - no space after ","
Functions defined above are available in this gist. They are based on code from the O’Reilly book “Introducing Elixir”.
** anonymous functions
In the shell only anonmyous functions can be used, e.g. bound to a variable.
Anonymous functions seem to behave similarly in the shell and in modules.
Anonymous functions do not behave quite the same as “defined” functions.
*** remember the extra dot
When using a bound anonymous function you must add an extra dot between the function name and its arguments:
iex(1)> ad5 = fn(x) -> 5+x end #Function iex(2)> ad5(9) ** (CompileError) iex:94: undefined function ad5/1 iex(3)> ad5.(9) 14
*** optional spaces almost everywhere, almost like defined functions
As with defined functions, there are lots of options for adding whitespace — but not quite the same options:
**** one parameter
iex(4)> a5b = fn (x) -> 5+x end #Function iex(5)> a5b.(9) 14 iex(6)> a5b. (9) 14 iex(7)> a5b. 9 ** (SyntaxError) iex:7: syntax error before: 9 # cf: iex(8)> :math.sqrt(9) 3.0 iex(9)> :math.sqrt (9) 3.0 iex(10)> :math.sqrt 9 3.0
*** two parameters
iex(11)> sma = fn(x,y) -> x+y end #Function iex(12)> smb = fn (x,y) -> x+y end #Function iex(13)> smc = fn x,y -> x+y end #Function iex(14)> smc.(4,5) 9 iex(15)> smc. (4,5) 9 iex(16)> smc. 4,5 ** (SyntaxError) iex:14: syntax error before: 4
cf similar defined functions in a module:
defmodule Clown do def clown3a(x,y) do IO.puts(x+y) end # def clown3b (x,y) do IO.puts(x+y) end # syntax error def clown3c x,y do IO.puts(x+y) end end
Running this module:
iex(17)> Drop.clown3a(5,6)
11
:ok
iex(18)> Drop.clown3a (5,6)
** (SyntaxError) iex:81: unexpected parentheses. If you are making a
function call, do not insert spaces between the function name and
the opening parentheses. Syntax error before: '('
iex(19)> Drop.clown3a 5,6
11
:ok
Command #15 is ok but command #18 is a syntax error;
Command #16 is a syntax error but command #19 is ok.
*** short version
There is also a short version of the anonymous function, in which the arity is implicit:
iex(20)> clownx = &(&1 + &2)
&:erlang.+/2
iex(21)> clownx. (4,5)
9
iex(22)> clownx. (4,5,6)
** (BadArityError) &:erlang.+/2 with arity 2 called with 3 arguments (4, 5, 6)
iex(23)> clowny = &(&1 + &3)
** (CompileError) iex:3: capture &3 cannot be defined without &2
(elixir) src/elixir_fn.erl:123: :elixir_fn.validate/4
(elixir) src/elixir_fn.erl:112: :elixir_fn.do_capture/4
The module compiler does do some of this checking. In the module below, the commented-out function c1a would be blocked by the compiler, but function c1b will not be. At runtime, the running process will crash when it reaches line 10.
defmodule Anon do
# def c1a(x,y,z) do
# f = &(&1 + &3) # syntax error blocked by compiler
# f. (x,y,z)
# end
def c1b(x,y,z) do
f = &(&1 + &2)
f. (x,y,z)
end
end
For example:
iex(24)> c("anon.ex")
[Anon]
iex(25)> Anon.c1b(2,3,4)
** (BadArityError) &:erlang.+/2 with arity 2 called with 3 arguments (2, 3, 4)
anon.ex:10: Anon.c1b/3