Если вы совсем не знакомы с Elm, то, если вкратце, это функциональный язык программирования и платформа для написания web-приложений. Код, написанный на Elm, компилируется в JavaScript и встраивается на страницу. Более подробно про основы можно почитать например здесь на Хабре или же просто на официальном сайте . Я же хотел суммировать свой опыт, который получил, написав своё первое приложение, поэтому в статье будет большое число очевидных вещей, чуть-чуть неочевидных и много ссылок.
Своё приложение я решил сделать после написания нескольких простеньких программ и прохождения туториала Elm Tutorial. Во время прочтения примеров из туториала и написаниях примеров было довольно сложно осознать все концепции сразу, поэтому я старался не останавливаться на сложных на тот момент мне местах (роутинг, композиция) и стремился получить готовый работающий сайт.
После этого я стал писать своё приложение, которое состоит из формы, где можно задать название события, опционально урл и дату, когда событие произойдёт. После сохранения все эти данные показываются в виде небольшой карточки с обратным отсчётом до события. Код можно найти здесь.
Язык, документация и источники информации
Очевидная вещь, хоть Elm и предназначен для фронтенда — это полностью другой язык по сравнению с JavaScript. Поэтому надо учить синтаксис, разбираться в концепциях. На официальном сайте есть небольшая дока по синтаксису, есть таблица сравнения с JavaScript. Полезно в самом начале почитать официальное руководство и попробовать запустить примеры оттуда. Есть еженедельная рассылка, где можно найти множество интересных статей, видео, примеров.
Поскольку до этого у меня практически не было опыта в использовании чистых функциональных языков программирования — мне было интересно и полезно читать материалы, которые объясняют общие концепции. Из таких материалов могу посоветовать серию из 6 статей So You Want to be a Functional Programmer, где подробно раскрываются основные концепции и книгу Mostly Adequate Guide (есть частичный перевод на русский).
Во время решения своей задачи мне очень помог пройденный туториал, пример из которого был под рукой. Он очень полезен, потому что там покрывается очень много аспектов создания сайта: от основ языка до сборки, арихтектуры, роутинга, взаимодействия с апи. Например, роутер оттуда я изначально перенёс в свою задачу практически без изменений. Потом уже, решая возникащие по ходу проблемы, разбирался как производится парсинг, как работают матчеры и так далее. Плюс этот туториал постоянно обновляет все примеры до последних версий языка (на текущий момент это 0.18).
С туториалами и примерами кода в интернете зачастую связана одна интересная особенность. В коде импортируются библиотеки, которые используют так называемый open import
. То есть встречается использование функции s
, а не UrlParser.s
. Это несколько усложняет чтение чужого кода. Всегда смотрите как, что и под каким именем импортируется в модуле. В свою очередь я сам стараюсь использовать qualified import
и это официальная рекомендация. Больше букв, но всегда сразу понятно какой используется тип или функция.
Советую обязательно подключить к вашему редактору Elm Format, который форматирует код согласно официальным гайдам. Elm Format кажется используется всеми и общий для всех синтаксис это действительно удобно.
Архитектура приложения
Важно изучить архитектуру, которая считается стандартом написания приложения: TEA (The Elm Architecture). Подробней можно прочитать в официальном руководстве. Если вы знакомы с Redux'ом то это не составит труда, а если нет, то в любом случае изучение этой концепции не кажется мне чем-то сложным. Есть три основые части: модель (хранит состояние приложения), апдейт (обработчик изменений, который модифицирует модель) и вид (внешний вид нашего приложения, отрендеренный в соответствии с состоянием модели).
Соответственно наше минимальное приложение инициализируется с этими тремя частями. В более сложных случаях к ним могут добавляться подписки, функция апдейта при изменения урла, флаги из javascript и возможно что-то ещё. Но база остаётся одной всегда.
Компилятор и отладка
Компилятор действительно работает отлично, ошибки показываются максимально информативные и всегда можно быть уверенным, что на выходе будет рабочая программа. Это, однако, не застраховывает от необходимости внимательно следить за кодом и смотреть, что и как работает. Я хочу сказать, что поначалу компилятор вызывает восторг и кажется, что это какая-то магия. Но когда начинаешь писать реальный код, то легко вернуть из функции неправильное значение правильного типа, что компилятор пропустит.
Код можно отлаживать используя модуль Debug. Хотя даже такое привычное для JavaScript действие выполняется не просто. Debug.log это функция, которая принимает строку (просто текст перед выводом) и значение и возвращает это значение, производя при этом сайд-эффект вывода в консоль. Debug.log нельзя использовать в любом месте программы и в любой части функции. То есть её можно поставить только в том месте, где уже есть какое-то значение.
В Elm 0.18 появился дебаггер, позволяющий отслеживать состояние приложения, возвращаться во времени назад и даже экспортировать состояние с историей для открытия в другом браузере. Я писал своё приложение на версии 0.17 и пока лично дебаггером не воспользовался.
Модули, масштабирование и рефакторинг
Самой большой и неразрешённой до конца для меня трудностью стало деление кода на модули. Большинство кода примеров лежит в одном файле. Официальный туториал в секции Scaling The Elm Architecture рассказывает лишь о делении на функции, базовое понятие модулей. Есть секция в туториале, есть другие примеры в интернете, но применить всё это с первого захода для своего проекта мне не удалось, так как были сложности с вставкой в модульную структуру стороннего компонента elm-datepicker, который я использую. На текущий момент я сделал разделение, которое пока что меня устраивает: основной код с апдейтами и функциями в главном файле, отдельно вынесены все типы и разметка страниц. Зато в процессе глобального рефакторинга своего приложения я обнаружил неожиданную вещь, о которой до этого много читал, но не осозновал. Рефакторинг делать абсолютно не страшно, а даже интересно. Вся система с типами, неизменяемыми данными, чистыми функциями и контролирующим это компилятором не даёт сломать код. Можно сколько угодно менять названия всего, менять структуру, создавать и удалять файлы и быть спокойным, что после успешной компиляции на выходе будет работающее приложение.
Выводы
Мне было интересно попробовать что-то сильно отличное от стандартной схемы программирования в JavaScript. Это было временами непросто, потому что функциональное программирование ломает мозг и для меня, как человека, который по большей части писал только на JavaScript — порог вхождения очень высок. Простые вещи, которые в Elm делаются совсем не просто — вроде получения даты, http-запроса, вызывают поначалу недоумение и боль. С постепенным овладеванием концепциями и инструментами становится и проще и интереснее.
Каким бы ни было будущее Elm'а, я думаю, что инвестиции своего времени в его изучение оправданы. Кажется, что идеи функционального программирования проще осознать, используя чистый функциональный язык, не имея возможности в случае проблем по-быстрому написать что-то не так. И потом, даже если продолжать работать только с JavaScript — можно переносить и использовать новые идеи, и понимать пользу использования в JS функционального подхода в общем и, например, таких библиотек как FlowType, immutable.js, Ramda.