Getting Started With Elixir: Sigils

When writing code with Elixir, I often find myself feeling like I’m writing code in Ruby. There are a number of things about the Elixir language that feel just like the Ruby language. Sigils are one of those things at first glance - and then I remember how cool it is that Elixir lets you write your own sigils. In this post, we’ll take a look at some of the built in Elixir sigils, and then wrap things up by writing our own custom sigil at the end.

First, What’s a Sigil?

According to Google, here’s the definition of a sigil:

an inscribed or painted symbol considered to have magical power.

In Elixir, much like in Ruby, a sigil is a shorthand name for a function, expressed using a tilde followed by an uppercase or lowercase letter, some kind of delimited content, and then a set of options.

Let’s take a look at some examples.

Lists

Lists come in handy frequently when working with Elixir, and building those lists is super easy using the ~w sigil.

iex> ~w(moana zootopia trolls)
["moana", "zootopia", "trolls"]

Elxiir uses the “white space” between each word in the expression we pass to the sigil to break the content apart and build a list for us.

If we’d rather build a list of atoms or characters, instead of strings, we can pass the optional a or c modifier to the sigil.

iex> ~w(moana zootopia trolls)a
[:moana, :zootopia, :trolls]

iex> ~w(moana zootopia trolls)c
['moana', 'zootopia', 'trolls']

Of course, we could always build the same list using a more traditional syntax, like this:

iex> ["moana", "zootopia", "trolls"]
["moana", "zootopia", "trolls"]

And we get the same results. But where is the fun in that!

Regular Expressions

Regular expressions can be created in a similar way using the ~r sigil. Here’s an example using Elixir’s Regex library. The regular expression to match against is defined using the ~r sigil, and then that is passed along to the Regex.match? function with the string to match against the expression:

iex> Regex.match?(~r/elixir is fun/, "elixir is fun to learn")
true

iex> Regex.match?(~r/elixir is fun/, "elixir")
false

This example just barely scratches the surfact of what is possible with the built in Regex module in Elixir. For additional examples and documentation, check out the full documentation for the Regex module.

Creating Your Own Sigils

Finally, the best part about sigils in the Elixir language (especially compared to their counterpart in Ruby), is the ability to create your own custom sigils. Almost everything in the Elixir language is extensible, and sigils are no exception.

Before we implement our own sigil, we need to learn one more fundamental thing about sigils: the built in sigils in Elixir are simple functions defined with the signature sigil_x(term, opts).

These sigil functions can be called using their shorthand name (i.e. ~w(<words>), like we have been) or using their full sigil name (i.e. sigil_w(<words>)). Both forms will produce the same results.

With that in mind, we can write our own custom sigils by defining functions that follow the same format.

For example, let’s assume we find ourselves continually wanting to create strings with all uppercase letters. According to the Elixir hexdocs, the string uppercase function in Elixir is String.upcase(binary).

We can shorten this by creating our own sigil. Let’s call it sigil_u.

defmodule MySigils do
  def sigil_u(string, _opts), do: String.upcase(string)
end

defmodule SigilTest do
  import MySigils

  def test_name do
    IO.puts ~u(moana)
    IO.puts ~u(zootopia)
    IO.puts ~u(tRoLls)
    IO.puts ~u(1230)
  end
end

SigilTest.test_name

In this block, we first define a new module named MySigils, where we can define all of our custom sigils. Inside that module, we define the sigil_u function, which accepts two parameters: the first parameter is the string we are going to manipulate, and the second is a set of options.

Options come in handy when you want to slightly modify the output of the sigil. For example, if we want to control whether or not string interpolation is performed by the function, we could provide an option to specify this behavior. For this example, we are going to skip any additional options, so we define the options parameter with a leading underscore so Elixir will know we intend to ignore it (i.e. _opts).

If we run this in an iex session, we should get uppercase versions of each string, just like we expect:

MOANA
ZOOTOPIA
TROLLS
1230

And that’s it! We now have a custom sigil that automatically returns an uppercase string representation of the value we pass into it.

Last Minute Thoughts On Creating Your Own Sigils

As I mentioned in the beginning of this post, I think having the ability to create your own custom sigils is cool, especially coming from a Ruby background where I didn’t have that option.

However, keep in mind that every custom sigil is another redirection point. One of our primary goals as software craftsman is writing clean and simple code, and we still want to hold true to that idea, even when using custom sigils.

If you find yourself getting ready to write your own sigil, first stop and ask yourself if a custom sigil is the right solution for the problem. Would it make more sense to implement this new functionality as an additional function on an existing module? Would it make more sense to implement this new functionality as an entirely new module?

At the end of the day, a sigil is really just a function with a single-letter name. Will the other developers on your team be able to understand your intent and follow your thought process, even if the function name is only one letter long?

With that said, custom sigils are pretty slick. I would just make sure you are very intentional about when you choose to use them, and use them sparingly when you do.

Remember…

“With great power comes great responsibility.” ~ Voltaire (or was it Uncle Ben?)