# Functions
Since Elixir is a *functional* programming language, functions are first-class citizens and the foundation for everything you build. You already saw how to define a function inside a module, but let's elaborate on this a bit.
You can define multiple function "clauses" that match against certain values. Function clauses are evaluated top-to-bottom, so the function clause at the top is evaluated first. If it doesn't match, the second function clause is evaluated, and so on. If one function matches, all functions that come thereafter are ignored.
```elixir
defmodule RunElixir.Checker do
# This clause will be evaluated first.
# It matches against integer values from 18 to 150.
# The >= and <= operators are called 'Guards'.
def adult?(age) when is_integer(age) and age >= 18 and age <= 150 do
true
end
# This clause will match integer values from 0 to 17.
# If 'age' was '18', the function clause above would have matched
# and this function clause would never get evaluated.
def adult?(age) when is_integer(age) and age >= 0 do
false
end
# This function will match all values that didn't match the function clauses above.
# This could be integer values outside the range from 0 to 150 or other data types.
#
# This is also how you write a one-line function without the do ... end notation.
def adult?(age), do: raise "Invalid age: #{age}"
end
```
## Default values
You can define default values using the `\\` symbol, like this:
```elixir
defmodule RunElixir.Profile do
@legal_age 18
def adult?(age, legal_age \\ @legal_age) do
age >= legal_age
end
end
```
Now, you can either use the default value or overwrite it like this:
```elixir
RunElixir.Profile.adult?(18) # => true
RunElixir.Profile.adult?(18, 21) # => false
```
If you want to define a default value for multiple function clauses, you need to create a `function head`, like this:
```elixir
defmodule RunElixir.ProfileChecker do
@legal_age 18
# This is the function head which defines the default value for all clauses. It has no body.
def adult?(age, legal_age \\ @legal_age)
def adult?(age, legal_age) when age >= legal_age do
true
end
def adult?(age, legal_age) when age >= 0 and age < legal_age do
false
end
def adult?(age, _legal_age), do: raise("Invalid age: #{age}")
end
# Again, you can either use the default value or overwrite it when you call the function:
RunElixir.ProfileChecker.adult?(18) # => true
RunElixir.ProfileChecker.adult?(18, 21) # => false
```
## Function Arity
Functions have an `arity`, which describes how many arguments they expect. For example, our `adult?` function has an arity of `2` because it expects two input arguments, so you would identify the function as `adult?/2`.
However, one of the arguments has a default value, so we actually define two functions, one with an arity of `1` (plus the default value but it doesn't count) and one with an arity of `2` if we overwrite the default value.
If you list all functions of the `Profile` module, you'd see that it has two `adult?` functions:
```elixir
RunElixir.Profile.__info__(:functions)
```
<!-- livebook:{"output":true} -->
```
[adult?: 1, adult?: 2]
```
## Anonymous Functions
You can define an anonymous function with `fn arguments -> body end`.
```elixir
add = fn a, b -> a + b end
add.(1, 2) # => 3
# A function without arguments
ran = fn -> Enum.random(1..100) end
ran.() # => 20
# You can also use anonymous functions as 'Closures' because they can 'close'
# around variables defined in the same scope and use them later.
value = 20
lazy_evaluate = fn div -> value * 10.0e10 / div end
lazy_evaluate.(2048) # => 976562500.0
# Even if you change the local variable after you define the anonymous function,
# the function will keep the old value.
value = 30
lazy_evaluate.(2048) # => 976562500.0
# A shorthand notation for anonymous functions:
# &1 is the first argument, &2 the second, and so on.
fun = &(&1 + &2)
fun.(4, 5) # => 9
```
You can assign an anonymous function to a variable and pass it around as an argument too. This comes in handy if you work with callbacks. In this example, we fetch a dad joke from an API using the HTTP library [Req](https://hexdocs.pm/req) and execute the callback function with the response.
```elixir
# First, we install the Req library.
Mix.install([{:req, "~> 0.5.0"}])
defmodule RunElixir.Jokes do
def get_dad_joke(callback_fn) do
# This will execute an anonymous function in an async process and
# execute the callback function with the result.
# We will discuss async functions later on.
spawn(fn ->
joke = Req.get!("https://icanhazdadjoke.com", headers: [accept: "text/plain"])
callback_fn.(joke)
end)
end
end
```
Now, let's see how you can pass an anonymous function as an argument.
```elixir
callback_fn = fn
# You can define multiple function clauses also in anonymous functions:
%Req.Response{status: 200, body: joke} -> IO.inspect("Here's a dad joke for you: #{joke}")
%Req.Response{status: status, body: message} -> IO.inspect("Oh no! An error occurred #{status} - #{message}")
end
RunElixir.Jokes.get_dad_joke(callback_fn)
```
<!-- livebook:{"output":true} -->
```
"Here's a dad joke for you: Why does Han Solo like gum? It's chewy!"
```
## Return values
There is no explicit `return` statement in Elixir and you cannot return early from a function. A function always returns the last statement in its body.
```elixir
fun = fn age ->
if age >= 18, do: :adult
:minor
end
fun.(18) # => :minor
fun.(17) # => :minor
```
<!-- livebook:{"output":true} -->
```
1: :minor
2: :minor
```
The default return value for a function is `nil`, so if you don't specify a return value explicitly, Elixir will return `nil` for you. This might surprise you in some cases:
```elixir
empty_fun = fn -> end
fun = fn age ->
# The 'if ... end' statement will return 'nil' if it doesn't evaluate to 'true'
# and you don't provide an 'else' clause.
if age >= 18 do
:adult
end
end
empty_fun.() # => nil
fun.(18) # => :adult
fun.(17) # => nil
```
The only exception to this is if a function raises an exception. In that case, the rest of the function is not executed:
```elixir
raises = fn ->
raise "Boom"
:return_something
end
raises.() |> IO.inspect()
```
<!-- livebook:{"output":true} -->
```
** (RuntimeError) Boom
```