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
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: