Do you want to pick up from where you left of?
Take me there

Sigils

Working with and creating sigils.

Sigils Overview

Elixir provides an alternative syntax for representing and working with literals, called sigils.

A sigil starts with a tilde ~ and is followed by an identifier and a pair of delimiters. Prior to Elixir 1.15, the identifier must be a single character. As of 1.15, the identifier may also be a string of multiple uppercase characters.

The Elixir core provides us with some built in sigils, however, it is possible to create our own when we need to extend the language.

A list of available sigils include:

A list of delimiters include:

Char List

The ~c and ~C sigils generate character lists respectively. For example:

iex> ~c/2 + 7 = #{2 + 7}/
'2 + 7 = 9'

iex> ~C/2 + 7 = #{2 + 7}/
'2 + 7 = \#{2 + 7}'

We can see the lowercased ~c interpolates the calculation, whereas the uppercased ~C sigil does not. We will see that this uppercase / lowercase sequence is a common theme throughout the built in sigils.

Regular Expressions

The ~r and ~R sigils are used to represent Regular Expressions. We create them either on the fly or for use within the Regex functions. For example:

iex> re = ~r/elixir/
~r/elixir/

iex> "Elixir" =~ re
false

iex> "elixir" =~ re
true

We can see that in the first test for equality, that Elixir does not match with the regular expression. This is because it is capitalized. Because Elixir supports Perl Compatible Regular Expressions (PCRE), we can append i to the end of our sigil to turn off case sensitivity.

iex> re = ~r/elixir/i
~r/elixir/i

iex> "Elixir" =~ re
true

iex> "elixir" =~ re
true

Further, Elixir provides the Regex API which is built on top of Erlang’s regular expression library. Let’s use Regex.split/2 with a regex sigil:

iex> string = "100_000_000"
"100_000_000"

iex> Regex.split(~r/_/, string)
["100", "000", "000"]

As we can see, the string "100_000_000" is split on the underscore thanks to our ~r/_/ sigil. The Regex.split function returns a list.

String

The ~s and ~S sigils are used to generate string data. For example:

iex> ~s/the cat in the hat on the mat/
"the cat in the hat on the mat"

iex> ~S/the cat in the hat on the mat/
"the cat in the hat on the mat"

What is the difference? The difference is similar to the Character List sigil that we looked at. The answer is interpolation and the use of escape sequences. If we take another example:

iex> ~s/welcome to elixir #{String.downcase "SCHOOL"}/
"welcome to elixir school"

iex> ~S/welcome to elixir #{String.downcase "SCHOOL"}/
"welcome to elixir \#{String.downcase \"SCHOOL\"}"

Word List

The word list sigil can come in handy from time to time. It can save both time, keystrokes and arguably reduce the complexity within the codebase. Take this simple example:

iex> ~w/i love elixir school/
["i", "love", "elixir", "school"]

iex> ~W/i love elixir school/
["i", "love", "elixir", "school"]

We can see that what is typed between the delimiters is separated by whitespace into a list. However, there is no difference between these two examples. Again, the difference comes with the interpolation and escape sequences. Take the following example:

iex> ~w/i love #{'e'}lixir school/
["i", "love", "elixir", "school"]

iex> ~W/i love #{'e'}lixir school/
["i", "love", "\#{'e'}lixir", "school"]

NaiveDateTime

A NaiveDateTime can be useful for quickly creating a struct to represent a DateTime without a timezone.

For the most part, we should avoid creating a NaiveDateTime struct directly. However, it is useful for pattern matching. For example:

iex> NaiveDateTime.from_iso8601("2015-01-23 23:50:07") == {:ok, ~N[2015-01-23 23:50:07]}

DateTime

A DateTime can be useful for quickly creating a struct to represent a DateTime with a UTC timezone. Since it’s in the UTC timezone and your string might represent a different timezone, a 3rd item is returned that represents the offset in seconds.

For example:

iex> DateTime.from_iso8601("2015-01-23 23:50:07Z") == {:ok, ~U[2015-01-23 23:50:07Z], 0}
iex> DateTime.from_iso8601("2015-01-23 23:50:07-0600") == {:ok, ~U[2015-01-24 05:50:07Z], -21600}

Creating Sigils

One of the goals of Elixir is to be an extendable programming language. It should come as no surprise then that you can easily create your own custom sigils. In this example, we will create a sigil to convert a string to uppercase. As there is already a function for this in the Elixir Core (String.upcase/1), we will wrap our sigil around that function.


iex> defmodule MySigils do
...>   def sigil_p(string, []), do: String.upcase(string)
...> end

iex> import MySigils
MySigils

iex> ~p/elixir school/
"ELIXIR SCHOOL"

First we define a module called MySigils and within that module, we created a function called sigil_p. As there is no existing ~p sigil in the existing sigil space, we will use it. The _p indicates that we wish to use p as the character after the tilde. The function definition must take two arguments, an input and a list.

Multi-character sigils

In Elixir 1.15 and above, sigil identifiers may also be a sequence of uppercase characters. This can be used to clarify what a sigil is doing by providing more context than a single character provides.

Following the structure of the previous example, we can define a sigil ~REV that reverses a string.


iex> defmodule MySigils do
...>   def sigil_REV(string, []), do: String.reverse(string)
...> end

iex> import MySigils
MySigils

iex> ~REV<foobar>
"raboof"

Note that for multi-character sigils, all characters must be uppercase. Sigil functions like sigil_rev or sigil_Rev would cause a SyntaxError on invocation.

Caught a mistake or want to contribute to the lesson? Edit this lesson on GitHub!