Skip to content

Latest commit

 

History

History
347 lines (255 loc) · 4.73 KB

File metadata and controls

347 lines (255 loc) · 4.73 KB

Elixir Koans - 12 Pattern Matching

import ExUnit.Assertions

One matches one

assert match?(1, ___)

Patterns can be used to pull things apart

[head | tail] = [1, 2, 3, 4]
assert head == ___
assert tail == ___

And then put them back together

head = 1
tail = [2, 3, 4]
assert ___ == [head | tail]

Some values can be ignored

[_first, _second, third, _fourth] = [1, 2, 3, 4]
assert third == ___

Strings come apart just as easily

"Shopping list: " <> items = "Shopping list: eggs, milk"
assert items == ___

Maps support partial pattern matching

%{make: make} = %{type: "car", year: 2016, make: "Honda", color: "black"}
assert make == ___

Lists must match exactly

assert_raise ___, fn ->
    [a, b] = [1, 2, 3]
end

So must keyword lists

kw_list = [type: "car", year: 2016, make: "Honda"]
[_type | [_year | [tuple]]] = kw_list
assert tuple == {___, ___}

The pattern can make assertions about what it expects

assert match?([1, _second, _third], ___)

Functions perform pattern matching on their arguments

def make_noise(%{type: "cat"}), do: "Meow"
def make_noise(%{type: "dog"}), do: "Woof"
def make_noise(_anything), do: "Eh?"

cat = %{type: "cat"}
dog = %{type: "dog"}
snake = %{type: "snake"}
assert make_noise(cat) == ___
assert make_noise(dog) == ___
assert make_noise(snake) == ___

And they will only run the code that matches the argument

name = fn
  "duck" -> "Donald"
  "mouse" -> "Mickey"
  _other -> "I need a name!"
end
assert name.("mouse") == ___
assert name.("duck") == ___
assert name.("donkey") == ___

Errors are shaped differently than successful results

dog = %{type: "barking"}

type =
  case Map.fetch(dog, :type) do
    {:ok, value} -> value
    :error -> "not present"
  end
assert type == ___

You can pattern match into the fields of a struct

defmodule Animal do
    @moduledoc false
    defstruct [:kind, :name]
end

%Animal{name: name} = %Animal{kind: "dog", name: "Max"}
assert name == ___

...or onto the type of the struct itself

defmodule Plane do
  @moduledoc false
  defstruct passengers: 0, maker: :boeing
  def plane?(%Plane{}), do: true
  def plane?(_), do: false
end
assert Plane.plane?(%Plane{passengers: 417, maker: :boeing}) == ___
assert Plane.plane?(%Animal{}) == ___

Structs will even match with a regular map

%{name: name} = %Animal{kind: "dog", name: "Max"}
assert name == ___

A value can be bound to a variable

a = 1
assert a == ___

A variable can be rebound

a = 1
a = 2
assert a == ___

A variable can be pinned to use its value when matching instead of binding to a new value

pinned_variable = 1

example = fn
  ^pinned_variable -> "The number One"
  2 -> "The number Two"
  number -> "The number #{number}"
end
assert example.(1) == ___
assert example.(2) == ___
assert example.(3) == ___

Pinning works anywhere one would match, including 'case'

pinned_variable = 1

result =
  case 1 do
    ^pinned_variable -> "same"
    other -> "different #{other}"
  end
assert result == ___

Trying to rebind a pinned variable will result in an error

a = 1
assert_raise MatchError, fn ->
  ^a = ___
end

Pattern matching works with nested data structures

user = %{
  profile: %{
    personal: %{name: "Alice", age: 30},
    settings: %{theme: "dark", notifications: true}
  }
}

%{profile: %{personal: %{age: age}, settings: %{theme: theme}}} = user
assert age == ___
assert theme == ___

Lists can be pattern matched with head and tail

numbers = [1, 2, 3, 4, 5]

[first, second | rest] = numbers
assert first == ___
assert second == ___
assert rest == ___
[head | _tail] = numbers
assert head == ___

Pattern matching can extract values from function return tuples

divide = fn
  _, 0 -> {:error, :division_by_zero}
  x, y -> {:ok, x / y}
end

{:ok, result} = divide.(10, 2)
assert result == ___
{:error, reason} = divide.(10, 0)
assert reason == ___

Next Steps