Tuples
https://hexdocs.pm/elixir/Tuple.html
Lists
# Literal Form
[]
[1]
[1, 2, 3]
[head | tail] = [1, 2, 3]
head == 1
tail == [2, 3]
https://hexdocs.pm/elixir/List.html
Maps
Ce sont les dictionnaires d’Elixir.
# If the key is an atom:
%{atom_key: 1}
# If the key is a different type:
%{1 => :atom_value}
# You can even mix these if the atom form comes second:
%{"first_form" => :a, atom_form: :b}
Voir documentation https://hexdocs.pm/elixir/Map.html
Modules attributes
defmodule Example do
# Defines the attribute as the value 1
@constant_number 1
def example_value() do
# Returns the value 1
@constant_number
end
end
Cond
cond do
x > 10 -> :this_might_be_the_way
y < 7 -> :or_that_might_be_the_way
true -> :this_is_the_default_way
end
Case
age = 15
case age do
0 -> 'infant'
age when age < 4 -> 'baby'
age when age < 13 -> 'child'
age when age < 18 -> 'teenager'
_ -> 'adult'
end
# => 'teenager'
Default argument
def guess(number \\ 5)
def guess(number) when number != 5, do: false
def guess(number) when number == 5, do: true
guess()
# => true
Guards
https://hexdocs.pm/elixir/master/patterns-and-guards.html#guards
def empty?(list) when is_list(list) and length(list) == 0 do
true
end
Multiple Clause Functions
# pattern matching the argument
def number(7) do
"Awesome, that's my favorite"
end
# using a guard
def number(n) when is_integer(n) do
"That's not my favorite"
end
def number(_n) do
"That's not even a number!"
end
Pattern Matching
https://exercism.org/tracks/elixir/concepts/pattern-matching
{:ok, number, _} = {:ok, 5, [4.5, 6.3]}
number
# => 5 is bound to this variable
- The pin operator
^
can be used to prevent rebounding a variable and instead pattern match against its existing value.
number = 10
{:ok, ^number, _} = {:ok, 5, [4.5, 6.3]}
# => ** (MatchError) no match of right hand side value: {:ok, 5, [4.5, 6.3]}
-
Pattern matches may also occur in a function clause head, so that only arguments that match the pattern will invoke the function.
-
Variables can be bound in a function clause pattern match.
Pipe Operator
The |>
operator is called the pipe operator. It can be used to chain function calls together in such a way that the value returned by the previous function call is passed as the first argument to the next function call.
String.replace_suffix(String.upcase(String.duplicate("go ", 3)), " ", "!")
# versus
"go "
|> String.duplicate(3)
|> String.upcase()
|> String.replace_suffix(" ", "!")
Char list
'hello'
[65, 66, 67]
# => 'ABC'
[first_letter | _] = 'cat'
first_letter
Strings
Strings in Elixir are delimited by double quotes, and they are encoded in UTF-8:
"Hi!"
Strings can be concatenated using the <>/2
operator:
"Welcome to" <> " " <> "New York"
# => "Welcome to New York"
Strings in Elixir support interpolation using the #{}
syntax:
"6 * 7 = #{6 * 7}"
# => "6 * 7 = 42"
Any Elixir expression is valid inside the interpolation. If a string is given, the string is interpolated as is. If any other value is given, Elixir will attempt to convert it to a string.
"Elixir can convert booleans to strings: #{true}"
# => "Elixir can convert booleans to strings: true"
"And #{["lists", ", ", "too!"]}"
# => "And lists, too!"
Recursion
Recursive functions are functions that call themselves.
A recursive function needs to have at least one base case and at least one recursive case.
A base case returns a value without calling the function again. A recursive case calls the function again, modifying the input so that it will at some point match the base case.
Very often, each case is written in its own function clause.
# base case
def count([]), do: 0
# recursive case
def count([_head | tail]), do: 1 + count(tail)
A recursive function can have many base cases and/or many recursive cases. For example the Fibonacci sequence is a recursive sequence with two base cases:
def fibonacci(0), do: 0
def fibonacci(1), do: 1
def fibonacci(n), do: fibonacci(n - 1) + fibonacci(n - 2)
Counting the number of occurrences of some given value x
in a list has two recursive cases:
def count_occurrences([], _x), do: 0
def count_occurrences([x | tail], x), do: 1 + count_occurrences(tail, x)
def count_occurrences([_ | tail], x), do: count_occurrences(tail, x)
Loops through recursion
Due to immutability, loops in Elixir are written differently from imperative languages. For example, loops commonly look like:
for(i = 0; i < array.size; i++) {
# do something with array[i]
}
In a functional language, mutating i
(by calling i++
) is not possible. Thus, loops have to be implemented with recursion.
The equivalent of a for
loop in Elixir would look like this:
def loop([]), do: nil
def loop([head | tail]) do
do_something(head)
loop(tail)
end
In practice, iterating over lists and other enumerable data structures is most often done using the Enum
module. Under the hood, functions from the Enum
module are implemented using recursion.
@spec types
https://hexdocs.pm/elixir/typespecs.html
Ces deux façon d’écrire sont équivalentes les @spec :
@spec format_address(address :: address()) :: String.t()
@spec format_address(address) :: String.t
def format_address(%{street: street, postal_code: postal_code, city: city}) do
format_address({street, postal_code, city})
end
def format_address({street, postal_code, city}) do
"""
#{String.upcase(street)}
#{String.upcase(postal_code)} #{String.upcase(city)}
"""
end