In Elixir, a tuple is a data structure which organizes data, holding a fixed number of any-type items, but without explicit names for each element. Tuples are often used in Elixir for memory read-intensive operations, since read-access of an element is a constant-time operation. They are not usually used when elements may need to be added/removed dynamically because rather than modifying the existing tuple, a new tuple is created which requires memory to be allocated upfront.
In practice, tuples are created in Elixir using curly braces, and element can be individually accessed using the elem/1
function using 0-based indexing:
empty_tuple = {}
one_element_tuple = {1}
multiple_element_tuple = {1, :a, "hello"}
elem(multiple_element_tuple, 2)
# => "hello"
Tuples are often used in practice to represent grouped information.
Float.ratio(0.25)
# => {1, 4} indicating the numerator and denominator of the fraction ¼
The use of pattern matching is dominant in assertive, idiomatic Elixir code. You might recall that =/2
is described as a match operator rather than as an assignment operator. When Elixir invokes the =/2
function, if the pattern on the left matches the right, any variables on the left are bound, and the function returns the value of the right side. A MatchError
is raised if there is no match.
2 = 2
# => 2
# Literals can be matched if they are the same
2 = 3
# => ** (MatchError) no match of right hand side value: 3
{_, denominator} = Float.ratio(0.25)
# => {1, 4}
# the variable `denominator` is bound to the value 4
Remember, matches occur from the right side to the left side.
In the last example if we don't need a variable in a pattern match, we can discard it by referencing _
. Any variable starting with an _
is not tracked by the runtime.
Pattern matching is also a useful tool when creating multiple function clauses. Pattern matching can be used on the functions' parameters which then determines which function clause to invoke -- starting from the top of the file down until the first match. Variables may be bound to patterns.
defmodule Example do
def named_function(:a = variable_a) do
{variable_a, 1}
end
def named_function(:b = variable_b) do
{variable_b, 2}
end
end
Example.named_function(:a)
# => {:a, 1}
Example.named_function(:b)
# => {:b, 2}
Example.named_function(:c)
# => ** (FunctionClauseError) no function clause matching in Example.named_function/1