Изначально я планировал выпустить этот опус как дополнение к книге “Learn You Some Erlang”, однако его содержание больше соответствует редакционному материалу, нежели хорошему справочному документу, поэтому я решил просто написать об этом в блоге.
Многие новички в мире Erlang успешно изучают его и начинают играться, не вступая с ним в тесное знакомство. Я прочитал много жалоб именно на синтаксис и эти “козьи шарики” (ant turds — ориг.) — «веселый» способ называть символы ,, ;, .. Например, «Как же они бесят», а также многое другое.
Я упоминал в книге, что Erlang берет свое начало из Prolog. Это дает нам понять, откуда берутся все эти знаки препинания, но это, увы, не заставляет людей проникнуться любовью к подобной пунктуации. И правда, почему-то никто не говорит мне: «А-а! Prolog! Что ж ты раньше не сказал!» Ввиду этого я предлагаю три возможных способа человеческого чтения кода на Erlang, дабы сделать мир добрее.
Мой любимый. Для того, чтобы постигнуть этот метод, нужно перестать смотреть на код как на набор строк и начать думать в Выражениях. Выражение — это любой кусок кода, который возвращает что-либо.
В командной строке Erlang знак точки (.) означает конец выражения. Чтобы выполнить выражение 2+2, нужно поставить точку (и затем нажать Enter), чтобы оно выполнилось и вернуло что-нибудь.
Однако при написании модуля символ точки заканчивает Формы. Формы — это атрибуты модуля или объявления функций. Форма ничего не возвращает и поэтому не является выражением.
Ввиду этих особенностей можно долго спорить на тему использования точки (.). Но я предлагаю забить на командную строку и не использовать в ней этот метод, а вместо этого запомнить, что точка должна заканчивать форму.
Но продолжим. Нам потребуется еще два правила:
Обобщив все вышесказанное, мы можем смело заключить, что если после выражения (например case...of...end) следует запятая (,), то затем обязано идти следующее выражение. Точка с запятой (;) ставится в конце ветки функции, а точка (.) на последней ветке функции.
Наше обычное представление, когда символом (;) мы разделяли строки (как, например, в C или Java), сейчас надо просто взять и выкинуть в окно. Вместо этого мы рассматриваем наш код как заполняемый шаблон (отсюда и название метода):
Указанные правила действительно работают, но нужно переключить свой мозг в иной режим чтения кода. Именно здесь заключается самый сложный переход: мы должны перейти от строк и блоков к предопределенному шаблону. Поясню: если вдуматься, то конструкции вроде
Не самый мой любимый способ, но я прекрасно понимаю, что люди по разному воспринимают логические конструкции. К тому же, этот метод часто хвалят.
Здесь мы сравниваем Erlang и язык (можно английский, можно русский). Представьте что вы составляете список вещей для поездки. Хотя нет, не будем представлять, вот он:
Если перевести это на язык Erlang, то все будет выглядеть очень похоже (К сожалению, в таком виде оно работать не будет, так как Erlang поддерживает UTF только в рамках строковых переменных, но никак не исходного кода. Там, по старинке, ISO-latin-1. — прим. пер.)
Видите, можно просто заменить предметы на выражения и вот оно! А вот выражения вида if … end следует представлять просто как вложенные списки таких вещей.
Еще один способ мне предложили на #erlang. В этом методе, увидев (,), надо читать “И”, (;) становится “ИЛИ” (как вариант, «А ЕЩЕ», — прим. пер.), а (.) означает “КОНЕЦ”. Таким образом объявление функции можно читать как набор вложенных логических предикатов и условий.
Некоторым крайне сложно принять «козьи шарики» за невозможность банально взять и поменять местами две строчки кода без их изменения. Как мне кажется, много способов интерпретации языка на человеческом уровне придумать сложно (да и вряд ли необходимо), но надеюсь, эта статья окажется кому-нибудь полезной. В конце концов, «синтаксис не сложный, он просто пугает».
Многие новички в мире Erlang успешно изучают его и начинают играться, не вступая с ним в тесное знакомство. Я прочитал много жалоб именно на синтаксис и эти “козьи шарики” (ant turds — ориг.) — «веселый» способ называть символы ,, ;, .. Например, «Как же они бесят», а также многое другое.
Я упоминал в книге, что Erlang берет свое начало из Prolog. Это дает нам понять, откуда берутся все эти знаки препинания, но это, увы, не заставляет людей проникнуться любовью к подобной пунктуации. И правда, почему-то никто не говорит мне: «А-а! Prolog! Что ж ты раньше не сказал!» Ввиду этого я предлагаю три возможных способа человеческого чтения кода на Erlang, дабы сделать мир добрее.
Шаблон
Мой любимый. Для того, чтобы постигнуть этот метод, нужно перестать смотреть на код как на набор строк и начать думать в Выражениях. Выражение — это любой кусок кода, который возвращает что-либо.
В командной строке Erlang знак точки (.) означает конец выражения. Чтобы выполнить выражение 2+2, нужно поставить точку (и затем нажать Enter), чтобы оно выполнилось и вернуло что-нибудь.
Однако при написании модуля символ точки заканчивает Формы. Формы — это атрибуты модуля или объявления функций. Форма ничего не возвращает и поэтому не является выражением.
Ввиду этих особенностей можно долго спорить на тему использования точки (.). Но я предлагаю забить на командную строку и не использовать в ней этот метод, а вместо этого запомнить, что точка должна заканчивать форму.
Но продолжим. Нам потребуется еще два правила:
- Запятая (,) разделяет выражения:
C = A+B, D = A+C
Просто как орех, не правда ли? Заметим, однако, что
— все суть выражения. Например, можно сделать следующее:if ... end case ... of ... end begin ... end fun() -> ... end try ... of ... catch ... end
И получить значение на выходе if...end. Это объясняет, почему мы порой видим запятые (,) после этих языковых конструкций — это всего лишь означает, что далее у нас следует еще одно выражение, которое надо выполнить.Var = if X > 0 -> valid; X =< 0 -> invalid end
- Точка с запятой (;) выполняет две функции.
- Она разделяет различные определения функции:
fac(0) -> 1; fac(N) -> N * fac(N-1).
- Она разделяет различные ветки выражений if … end, case … of … end и других:
if X < 0 -> negative; X > 0 -> positive; X == 0 -> zero end
Может показаться странным, что последняя ветка выражения не отделяется (;). Но ведь (;) разделяет ветки, но не заканчивает каждую из них. Надо думать в выражениях, а не в строчках. Некоторым легче читать код, если разделитель ставить следующим образом:
if X < 0 -> negative ; X > 0 -> positive ; X == 0 -> zero end
Такое форматирование лучше подчеркивает роль (;) именно как разделителя. Этот знак идет между ветками и условиями, а не после них.
- Она разделяет различные определения функции:
Обобщив все вышесказанное, мы можем смело заключить, что если после выражения (например case...of...end) следует запятая (,), то затем обязано идти следующее выражение. Точка с запятой (;) ставится в конце ветки функции, а точка (.) на последней ветке функции.
Наше обычное представление, когда символом (;) мы разделяли строки (как, например, в C или Java), сейчас надо просто взять и выкинуть в окно. Вместо этого мы рассматриваем наш код как заполняемый шаблон (отсюда и название метода):
head1(Args) [Guard] ->
Expr11, Expr12, …, Expr1N;
head2(Args) [Guard] ->
Expr21, Expr22, …, Expr2N;
headM(Args) [Guard] ->
ExprM1, ExprM2, …, ExprMN.
%% Где ExprIJ - это выражения
Указанные правила действительно работают, но нужно переключить свой мозг в иной режим чтения кода. Именно здесь заключается самый сложный переход: мы должны перейти от строк и блоков к предопределенному шаблону. Поясню: если вдуматься, то конструкции вроде
for (int i = 0; i >= x; i ++) { … }
// Или даже
for (...);
имеют весьма странный синтаксис по сравнению ко многим другим конструкциям в других языках. Просто мы настолько привыкли к подобным сооружениям, что совершенно спокойно воспринимаем их.Предложение
Не самый мой любимый способ, но я прекрасно понимаю, что люди по разному воспринимают логические конструкции. К тому же, этот метод часто хвалят.
Здесь мы сравниваем Erlang и язык (можно английский, можно русский). Представьте что вы составляете список вещей для поездки. Хотя нет, не будем представлять, вот он:
Мне нужны следующие вещи в поездке:
Если будет солнечно, то крем от загара, вода, шляпа;
Если будет дождь, то зонтик, куртка;
Если будет ветер, то воздушный змей, рубашка.
Если перевести это на язык Erlang, то все будет выглядеть очень похоже (К сожалению, в таком виде оно работать не будет, так как Erlang поддерживает UTF только в рамках строковых переменных, но никак не исходного кода. Там, по старинке, ISO-latin-1. — прим. пер.)
вещи(солнце) ->
крем, вода, шляпа;
вещи(дождь) ->
зонтик, куртка;
вещи(ветер) ->
змей, рубашка.
Видите, можно просто заменить предметы на выражения и вот оно! А вот выражения вида if … end следует представлять просто как вложенные списки таких вещей.
И, Или, Конец
Еще один способ мне предложили на #erlang. В этом методе, увидев (,), надо читать “И”, (;) становится “ИЛИ” (как вариант, «А ЕЩЕ», — прим. пер.), а (.) означает “КОНЕЦ”. Таким образом объявление функции можно читать как набор вложенных логических предикатов и условий.
В заключение...
Некоторым крайне сложно принять «козьи шарики» за невозможность банально взять и поменять местами две строчки кода без их изменения. Как мне кажется, много способов интерпретации языка на человеческом уровне придумать сложно (да и вряд ли необходимо), но надеюсь, эта статья окажется кому-нибудь полезной. В конце концов, «синтаксис не сложный, он просто пугает».