Сигилы

Мы уже знаем, что в Эликсирe есть строки с двойными кавычками и списки символов с одинарными кавычками. Однако, есть и другие структуры, у которых есть текстовое представление. Например, атомы, которые в основном создаются как :atom.

Одна из целей Эликсира – быть расширяемым. Разработчики должны иметь возможность расширить язык таким образом, чтобы решить любую задачу. Компьютерные науки стали слишком широкой областью, чтобы ядро любого языка смогло вместить в себя все решения всех возможных проблем. Однако, если сделать язык расширяемым, то разработчики, компании и сообщества сами смогут добавить в него все необходимое.

В текущей главе, мы рассмотрим сигилы – один из встроенных в язык механизмов для работы с текстовыми представлениями. Сигилы начинаются с символа тильда (~), после которого идет буква (которая идентифицирует сигил) и разделитель. Опционально, после разделителя могут быть добавлены еще и модификаторы.

Регулярные выражения

Самый часто используемый сигил в Эликсире – сигил регулярных выражений ~r:

# Регулярное выражение, которое находит в строке "foo" или "bar":
iex> regex = ~r/foo|bar/
~r/foo|bar/

iex> "foo" =~ regex
true

iex> "bat" =~ regex
false

Эликсир предоставляет совместимый с Перл синтаксис регулярных выражений, который реализован в библиотеке PCRE. Регулярные выражения поддерживают модификаторы. Например, модификатор i делает регулярное выражение нечувствительным к регистру:

iex> "HELLO" =~ ~r/hello/
false

iex> "HELLO" =~ ~r/hello/i
true

В модуле Regex доступно больше информации о других модификаторах и операциях с регулярными выражениями.

Пока что во всех примерах мы использовали / для разделения сигилов. Однако, сигилы поддерживают 8 разных типов разделителей:

~r/hello/
~r|hello|
~r"hello"
~r'hello'
~r(hello)
~r[hello]
~r{hello}
~r<hello>

Причина, по которой сигилы поддерживают разные типы разделителей, заключается в возможности писать код без экранирования. Регулярное выражение с прямыми слешами записанное в виде ~r(^https?://) читается гораздо проще, чем такое же выражение в другой форме записи ~r/^https?:\/\//. Аналогично, если в регулярном выражении есть прямые слеши и группы (которые используют ()), то можно использовать двойные кавычки вместо слешей.

Строки, списки символов и списки слов

Кроме регулярных выражений в языке Эликсир есть три других сигила.

Строки

Сигил ~s используется для создания строк, также как и двойные кавычки. Сигил ~s полезен, когда в самой строке есть двойные кавычки:

iex> ~s(this is a string with "double" quotes, not 'single' ones)
"this is a string with \"double\" quotes, not 'single' ones"

Списки символов

Сигил ~c используется для создания списка символов, содержащих одинарную кавычку:

iex> ~c(this is a char list containing 'single quotes')
'this is a char list containing \'single quotes\''

Списки слов

Сигил ~w используется для создания списка слов (сами слова – обычные строки). Внутри сигила ~w слова разделены пробелами:

iex> ~w(foo bar bat)
["foo", "bar", "bat"]

Сигил ~w поддерживает модификаторы c, s и a (списки символов, строки и атомы соответственно) для указания типа данных членов итогового списка:

iex> ~w(foo bar bat)a
[:foo, :bar, :bat]

Кроме сигилов в нижнем регистре, Эликсир также поддерживает сигилы в верхнем регистре, чтобы работать с экранированием и интерполяцией. Оба сигила ~s и ~S вернут строку, с той разницей, что сигилы в верхнем регистре не поддерживают экранирование и интерполяцию:

iex> ~s(String with escape codes \x26 #{"inter" <> "polation"})
"String with escape codes & interpolation"

iex> ~S(String without escape codes \x26 without #{interpolation})
"String without escape codes \\x26 without \#{interpolation}"

Список поддерживаемых символов экранирования:

  • \\ – одинарная обратная косая черта;
  • \a – звонок/тревога;
  • \b – клавиша возврата;
  • \d – удаление;
  • \e – выход;
  • \f – форма подачи;
  • \n – новая строка;
  • \r – возврат каретки;
  • \s – пробел;
  • \t – табуляция;
  • \v – вертикальная табуляция;
  • \0 – нулевой байт;
  • \xDD – один байт в восьмеричной записи (например \x13);
  • \uDDDD и \u{D...} – символ Юникода (например \u{1F600}).

Вдобавок, двойная кавычка внутри двойной кавычки должна быть экранирована как \", аналогично и одинарная кавычка внутри одинарной кавычки \'. Тем не менее, лучше изменить разделители, чем использовать экранирование.

Сигилы также поддерживают heredoc-синтаксис, в качестве разделителей используются три двойных или одинарных кавычки:

iex> ~s"""
...> this is
...> a heredoc string
...> """

Чаще всего heredoc-сигилы используют для написания документации. Например, если в документации встречается какой-то символ экранирования, то в документации могут появиться ошибки, потому что придется экранировать его дважды:

@doc """
Конвертирует двойные кавчки в одинарные.

## Примеры

    iex> convert("\\\"foo\\\"")
    "'foo'"

"""
def convert(...)

Но если использовать сигил ~S, то такой проблемы можно полностью избежать:

@doc ~S"""
Конвертирует двойные кавчки в одинарные.

## Примеры

    iex> convert("\"foo\"")
    "'foo'"

"""
def convert(...)

Собственные сигилы

Как уже было сказано в самом начале, сигилы – расширяемы. На самом деле использование сигила ~r/foo/i равнозначно вызову sigil_r с двоичными данными и списком символов в качестве аргументов:

iex> sigil_r(<<"foo">>, 'i')
~r"foo"i

Мы можем обратиться к документации сигила ~r через sigil_r:

iex> h sigil_r
...

Можно создавать собственные сигилы: нужно создать функцию, которая бы следовала формату sigil_{имя_индентификатора}. К примеру, давайте создадим сигил ~i, который возвращает целое число (с опциональным модификатором n, чтобы сделать число отрицательным):

iex> defmodule MySigils do
...>   def sigil_i(string, []), do: String.to_integer(string)
...>   def sigil_i(string, [?n]), do: -String.to_integer(string)
...> end

iex> import MySigils

iex> ~i(13)
13

iex> ~i(42)n
-42

Сигилы могут быть использованы для выполнения работы на шаге компиляции. К примеру, регулярные выражения в Эликсире компилируются в производительные структуры, чтобы работать быстрее на шаге выполнения. Если вам интересен данный вопрос, то советуем изучить, как работают макросы, и посмотреть как реализованы сигилы в модуле Kernel (где реализованы все sigil_* функции).

© 2020 / Россия Любые мысли и вопросы пишите на elixir@wunsh.ru.