DEV Community

Nick Vernij
Nick Vernij

Posted on • Updated on • Originally published at nickvernij.nl

7 ways to write logic with Elixir's pattern matching

I have started using Elixir a little less than a year ago, looking back at this year I have learned a lot about functional programming and Elixir in particular. We currently use Elixir in production, as a graphQL back-end, and it works like a charm.

The feature I love most is pattern matching, there are so many possibilities where I find ways to reduce the use of nested if-else blocks.

To quickly explain pattern matching, here is a little example:

# called with 1 apple
def say_apples(1), do: IO.puts("You have 1 apple")
# called with any other amount that 1
def say_apples(amount), do: IO.puts("You have " <> amount <> "apples")
Enter fullscreen mode Exit fullscreen mode

By defining a literal as a function argument, we are matching against that literal. Meaning that when I call say_apples with a first argument of 1, it will execute the method has a matching 1 in its method definition.

Using an identifier, as you normally would when writing methods, will match with anything. Be aware that matching happens in the order you function are written in. When your first method definition matches with everything, any methods below will never be called.

So let's get to the point, here are 7 examples!

#1 "When" keyword

# true when the number is lower than 10
def lower_than_ten(number) when number < 10, do: true
# false when the number is higher than 10
def lower_than_ten(number), do: false
Enter fullscreen mode Exit fullscreen mode

The when keyword can be used to execute expressions when matching, for example comparing the given argument.

#2 Matching struct types

# prints the name of the type
def print_type_name(%User{}), do: IO.puts("user")
def print_type_name(%Post{}), do: IO.puts("post")
Enter fullscreen mode Exit fullscreen mode

While I won't go into the details of Elixir's type system, you'll want to match whether a struct is a certain type at some point.

#3 Extracting variables from structs

# says hi to our user
def say_hi(%User{name: name}) do
    IO.puts("Hiya, " <> name)
end
Enter fullscreen mode Exit fullscreen mode

Structs are basically a key-value store, and sometimes you need one specific variable. I feel like in those cases, the above example reads way better than doing user.name in the method body.

#4 Pattern match of variable

# returns whether is_admin is true
def can_administer(%User{is_admin: true}), do: true
def can_administer(_), do: false
Enter fullscreen mode Exit fullscreen mode

We can even pattern match on the value inside struct properties. Woah! Also notice how we use a _ if we don't really wanna use a variable but still want to match with everything.

I could just return is_admin, I know, but this is an example :)

#5 String concatenation

# returns true if a string starts with "foo"
def starts_with_foo("foo" <> _), do: true
def starts_with_foo(_), do: false
Enter fullscreen mode Exit fullscreen mode

We can even use the concatenation operator in a method signature, to see if a string is prefixed with a certain string.

#6 First item in an array

# returns the first item in an array
def first([f | _]), do: f
Enter fullscreen mode Exit fullscreen mode

first([1, 2, 3]) will return 1

#7 Everything but the first item in an array

# returns everything but the first item in an array
def tail([_ | t]), do: t
Enter fullscreen mode Exit fullscreen mode

tail([1, 2, 3]) will return [2, 3]

The possibilities are endless! What are your favorite Elixir features?

Top comments (3)

Collapse
 
joshnuss profile image
Joshua Nussbaum

Nice article!
Another useful pattern match is multiple first elements of an array

def first([a, b, c | _]) do
  # ...
end
Collapse
 
nielsbom profile image
Niels Bom

I haven’t used it in a bigger project yet, but processes and OTP look extremely useful :-)

Collapse
 
nickforall profile image
Nick Vernij

Definitely! We have been using it in a pretty big project for about a year now, and being able to delegate tasks to processes (genservers) has been a really useful tool.