IO.puts(~s(She said "hi"))
She said "hi"
Sigils are literals that start with ~, a letter,
and a pair of delimiters. They're shortcuts for
common kinds of text and data.
~s builds a string, handy when it contains
double quotes.
IO.puts(~s(She said "hi"))
She said "hi"
~w splits on whitespace into a list of words.
The a modifier makes atoms instead of strings.
IO.inspect(~w(red green blue))
IO.inspect(~w(red green blue)a)
["red", "green", "blue"]
[:red, :green, :blue]
~r is a regular expression (PCRE). Modifiers like
i (case-insensitive) go after the delimiter.
IO.inspect(String.match?("Elixir", ~r/elixir/i))
IO.inspect(Regex.run(~r/(\d+)-(\d+)/, "range: 10-20"))
IO.inspect(String.replace("a1b22c333", ~r/\d+/, "#"))
true
["10-20", "10", "20"]
"a#b#c#"
Named captures come back as a map.
IO.inspect(Regex.named_captures(~r/(?<user>\w+)@(?<host>[\w.]+)/, "ada@example.com"))
%{"host" => "example.com", "user" => "ada"}
~c is a charlist, a list of integer codepoints.
You mostly meet these when calling Erlang
libraries.
IO.inspect(~c"abc")
IO.inspect(~c"abc" == [97, 98, 99])
~c"abc"
true
Date and time sigils are checked at compile time,
so an invalid date like ~D[2026-02-30] won't
compile.
date = ~D[2026-11-07]
IO.inspect(date)
IO.inspect(Date.add(date, 30))
IO.inspect(~T[09:30:00])
IO.inspect(~U[2026-11-07 09:30:00Z])
~D[2026-11-07]
~D[2026-12-07]
~T[09:30:00]
~U[2026-11-07 09:30:00Z]
Uppercase sigils don't interpolate or process escapes, which is nice for regexes and templates.
name = "Ada"
IO.puts(~s(hello #{name}\n) |> String.trim())
IO.puts(~S(hello #{name}\n))
hello Ada
hello #{name}\n
Any delimiter pair works, so pick one that doesn't clash with the content.
IO.puts(~s{paths like "/a/b" are easy})
IO.puts(~s|and (parens) too|)
paths like "/a/b" are easy
and (parens) too
A custom sigil is a function named sigil_x (or a
multi-letter uppercase name) that receives the
content and the modifiers. import the module to
use it. (In a script, the importing code must live
in a separate module from the definition.)
defmodule Sigils do
def sigil_i(text, []), do: String.to_integer(text)
def sigil_i(text, [?n]), do: -String.to_integer(text)
end
defmodule Demo do
import Sigils
def run do
IO.inspect(~i(42))
IO.inspect(~i(42)n)
end
end
Demo.run()
42
-42
Pitfall:
~w(...)acreates atoms. Fine for literals you write, but never build atom lists from user input (atoms aren't garbage collected).
Pitfall:
'abc'in single quotes is a charlist, not a string, and"abc" == ~c"abc"isfalse. Single-quoted charlists are deprecated in modern Elixir (the compiler warns); older code and some agents still write them. Use~c"..."when an Erlang API wants a charlist, and"..."otherwise.