Subscribe to access all episodes. View plans →
Published February 7, 2018
Elixir 1.6
In this episode let’s look at how to use guards in Elixir.
While guards aren’t limited to only function clauses, that’s what we’ll be focusing on in this episode.
Using guards in our function clause will allow us to set the rules for when a function is executed based on the arguments passed in.
To get started let’s look at the Elixir docs to see what expressions we can use to build guards.
We can combine any of these expressions to build a guard clause.
For our example we have a Shirt
module and inside we have three different lists containing different colors available for small, medium”, and large sized shirts.
Let’s create function named colors_for
that will take one argument - the size of a shirt - and return a list with the available colors for that size.
The sizes we’ll support are small, medium, and large.
Let’s start by implementing colors_for
for small.
We’ll take the size
and use the when
keyword to start our guard.
In most cases the size
will be the integer representation. 1
represents a “small” shirt size. So let’s include that condition.
A shirt size can also be string of a lowercase "s"
or capitalized "S"
.
So let’s update our guard to account for the lowercase "s"
Or the capitalized "S"
.
Great now our colors_for
function will trigger for our small sizes.
Let’s now create another color_for
function for medium sizes.
It will support our integer representation for medium - 2
. As well as a lowercase "m"
or a capitalized "M"
Then let’ create our colors_for
function for large sizes.
Our integer representation for them is 3
. As well as a lowercase "l"
and a capitalized "L"
.
shirt.exs
defmodule Shirt do
def colors_for(size) when size == 1 or size == "s" or size == "S" do
["Green", "Blue", "Pink"]
end
def colors_for(size) when size == 2 or size == "m" or size == "M" do
["Red", "Blue"]
end
def colors_for(size) when size == 3 or size == "l" or size == "L" do
["Red", "Black", "Blue", "Yellow"]
end
end
Let’s test it out. We’ll go to the command line and start iex
with our module.
$ iex shirt.exs
> Shirt.colors_for(1)
["Green", "Blue", "Pink"]
> Shirt.colors_for("s")
["Green", "Blue", "Pink"]
> Shirt.colors_for("M")
["Red", "Blue"]
> Shirt.colors_for("l")
["Red", "Black", "Blue", "Yellow"]
Great, our sizes are returned as expected.
Now that we’re successfully using guard clauses to determine which colors_for
function to trigger, let’s imagine we needed to use this same guard in many different modules of our application.
While we could copy the same code we have here, there’s a better way.
As of Elixir 1.6 we can create custom guards.
Let’s take our guards here and turn them into custom guards that we can name and easily use in different modules.
We’ll create a new module to house our guards named SizeTranslator
that we’ll define above our Shirt
module.
Let’s open the docs to see how we can create a guard.
We see that we use defguard
to create our guard.
We’ll give it a name, followed by the when
keyword and then our conditions.
Let’s go back to our module.
And create our first custom guard called is_small
passing in our our size
Then we’ll copy the conditions from below.
Let’s do the same for when a shirt is medium
And for when it’s large.
shirt.exs
defmodule SizeTranslator do
defguard is_small(size) when size == 1 or size == "s" or size == "S"
defguard is_medium(size) when size == 2 or size == "m" or size == "M"
defguard is_large(size) when size == 3 or size == "l" or size == "L"
end
defmodule Shirt do
import SizeTranslator
def colors_for(size) when is_small(size) do
["Green", "Blue", "Pink"]
end
def colors_for(size) when is_medium(size) do
["Red", "Blue"]
end
def colors_for(size) when is_large(size) do
["Red", "Black", "Blue", "Yellow"]
end
end
Then we can update our Shirt
module to use our new guards.
We’ll update each function to use it’s respective guard we wrote.
Since we’re now using the guards in this module we’ll need to import them, so let’s import SizeTranslator
.
Then let’s go back to our iex
session and reload our module.
And let’s try our functions again.
> r(Shirt)
{:reloaded, Shirt, [SizeTranslator, Shirt]}
> Shirt.colors_for(1)
["Green", "Blue", "Pink"]
> Shirt.colors_for(2)
["Red", "Blue"]
> Shirt.colors_for(3)
["Red", "Black", "Blue", "Yellow"]
> Shirt.colors_for("S")
["Green", "Blue", "Pink"]
And great our new custom guards are working and we’re seeing the expected results.