Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Pattern Matching

elixier_notebooks/PatternMatching.livemd

Pattern Matching

Match Operator ^

Are you ready for a curveball? In Elixir, the = operator is actually a match operator, comparable to the equals sign in algebra. Writing it turns the whole expression into an equation and makes Elixir match the values on the left hand with the values on the right hand. If the match succeeds, it returns the value of the equation. Otherwise, it throws an error. Let’s take a look:

x = 1
1

Now let’s try some simple matching:

1 = x
1
2 = x

Let’s try that with some of the collections we know:

list = [1, 2, 3]
[1, 2, 3]
[1, 2, 3] = list
[1, 2, 3]
[] = list
[1 | tail] = list
[1, 2, 3]
tail
[2, 3]
[2 | _] = list
{:ok, value} = {:ok, "Successful!"}
{:ok, "Successful!"}
value
"Successful!"
{:ok, value} = {:error}

Pin Operator

The match operator performs assignment when the left side of the match includes a variable. In some cases this variable rebinding behavior is undesirable. For these situations we have the pin operator: ^.

When we pin a variable we match on the existing value rather than rebinding to a new one. Let’s take a look at how this works:

x = 1
1
^x = 2
{x, ^x} = {2, 1}
{2, 1}
x
2

Elixir 1.2 introduced support for pins in map keys and function clauses:

key = "hello"
"hello"
%{^key => value} = %{"hello" => "world"}
%{"hello" => "world"}
value
"world"
%{^key => value} = %{:hello => "world"}

Function Clauses:

Pattern matching provides an excellent mechanism to guard against incorrect inputs in Elixir. You can define multiple function clauses with specific patterns to handle different input scenarios. When a function is called, Elixir will try to match the input with the patterns in each clause.

defmodule FunctionClauseExample do
  def handle_message({:new_user, name}) do
    IO.puts("Welcome new user, #{name}!")
  end
end
{:module, FunctionClauseExample, <<70, 79, 82, 49, 0, 0, 7, ...>>, {:handle_message, 1}}
correct_input = {:new_user, "Tim"}
FunctionClauseExample.handle_message(correct_input)
Welcome new user, Tim!
:ok
wrong_input = {:old_user, "Tom"}
FunctionClauseExample.handle_message(wrong_input)

We can implement multiple functions with different argument numbers or differnt clauses

defmodule MultipleFunctionClauseExample do
  def handle_message({:new_user, name}) do
    IO.puts("Welcome new user, #{name}!")
  end

  def handle_message({:old_user, name}) do
    IO.puts("Welcome old user, #{name}!")
  end
end
{:module, MultipleFunctionClauseExample, <<70, 79, 82, 49, 0, 0, 8, ...>>, {:handle_message, 1}}
{:new_user, "Tim"}
|> MultipleFunctionClauseExample.handle_message()
Welcome new user, Tim!
:ok
{:old_user, "Tom"}
|> MultipleFunctionClauseExample.handle_message()
Welcome old user, Tom!
:ok