[Перевод] Немного о синтаксисе Erlang
Изначально я планировал выпустить этот опус как дополнение к книге «Learn You Some 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 — все суть выражения. Например, можно сделать следующее: var = if X > 0 → valid; X =< 0 -> invalid end И получить значение на выходе if…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. В этом методе, увидев (,), надо читать «И», (;) становится «ИЛИ» (как вариант, «А ЕЩЕ», — прим. пер.), а (.) означает «КОНЕЦ». Таким образом объявление функции можно читать как набор вложенных логических предикатов и условий.
В заключение… Некоторым крайне сложно принять «козьи шарики» за невозможность банально взять и поменять местами две строчки кода без их изменения. Как мне кажется, много способов интерпретации языка на человеческом уровне придумать сложно (да и вряд ли необходимо), но надеюсь, эта статья окажется кому-нибудь полезной. В конце концов, «синтаксис не сложный, он просто пугает».