Если вы совсем не знакомы с Elm, то, если вкратце, это функциональный язык программирования и платформа для написания web-приложений. Код, написанный на Elm, компилируется в JavaScript и встраивается на страницу. Более подробно про основы можно почитать например здесь на Хабре или же просто на официальном сайте . Я же хотел суммировать свой опыт, который получил, написав своё первое приложение, поэтому в статье будет большое число очевидных вещей, чуть-чуть неочевидных и много ссылок.


Своё приложение я решил сделать после написания нескольких простеньких программ и прохождения туториала Elm Tutorial. Во время прочтения примеров из туториала и написаниях примеров было довольно сложно осознать все концепции сразу, поэтому я старался не останавливаться на сложных на тот момент мне местах (роутинг, композиция) и стремился получить готовый работающий сайт.


После этого я стал писать своё приложение, которое состоит из формы, где можно задать название события, опционально урл и дату, когда событие произойдёт. После сохранения все эти данные показываются в виде небольшой карточки с обратным отсчётом до события. Код и ссылку на github pages можно найти здесь.


Язык, документация и источники информации


Очевидная вещь, хоть 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.

Поделиться с друзьями
-->

Комментарии (37)


  1. ad3w
    20.12.2016 18:20

    Прямо сейчас делаю второй Elm-проект на заказ :) В целом доволен, даже очень большие проекты получаются логичными и понятными.


    1. dontpanic
      20.12.2016 18:30

      Круто! Мне надо ещё практиковаться) А как вы примерно делите код на модули? У меня дело пока не ушло дальше вынесения функций для вьюх в отдельные файлы.


      1. asci
        20.12.2016 19:09
        +2

        Я тем же вопросом задавался и набрел на вот этот гайд http://blog.jenkster.com/2016/04/how-i-structure-elm-apps.html но пока не применил его


      1. ad3w
        20.12.2016 19:46

        До этого разделял с помощью translator pattern, на который кинули ссылку ниже. Но сказать, что это усложняет приложение — ничего не сказать. Это надо попробовать :)

        В итоге решил полностью отказаться в пользу обычного разбиения на функции.


  1. pure_evil
    20.12.2016 18:37

    Самой большой и неразрешённой до конца для меня трудностью стало деление кода на модули.

    Полностью поддерживаю, для себя нашел translator pattern , но он довольно усложняет приложение. Может в будущем будут варианты получше.


  1. seryh
    20.12.2016 18:43

    То что язык можно применять только под фронтенд, не мотивирует тратить время на изучение. Выбрал для себя Clojure/ClojureScript так как позволяет покрыть очень широкий круг задач. Из плюшек дает все тоже самое — функциональщина, иммутабельность, легкий рефакторинг, мощный REPL. Проблем с разделением кода на модули нет никаких, команда пакетного менеджера lein new re-frame <project-name> создаст всю структуру, а документация к re-frame подробно все разжует. Если под задачу не подходит архитектура, то есть масса других шаблонов.


    1. wheercool
      20.12.2016 18:51

      Elm обладает развитой системой типов с автоматическим выводом, в ClojureScript такой возможности к сожалению нет (. Можете пояснить что вы подразумеваете под легким рефакторингом в Clojure? Он же вроде как динамически типизирован?
      PS я не против Clojure, просто интересно )


      1. seryh
        20.12.2016 18:59

        Elm обладает развитой системой типов с автоматическим выводом,

        В ClojureScript версии 1.9 появился spec, можно очень гибко и удобно покрыть весь код типами если нужно.


        Можете пояснить что вы подразумеваете под легким рефакторингом в Clojure.

        Можно легко перекраивать приложение, тут множество положительных факторов для этого — иммутабельность, REPL, хот-релоад без потери стейта через figwheel, мощь s-выражений. Попробуйте )


        1. am-amotion-city
          21.12.2016 09:33

          хот-релоад

          Кложа же в jvm выполняется, откуда там hot-reload? Или я что-то упустил?


          1. seryh
            21.12.2016 09:37

            Компиляция на лету в JS https://github.com/bhauman/lein-figwheel


            1. am-amotion-city
              21.12.2016 09:41

              А, в браузер, в dev-моде...


              Про это даже странно упоминать сейчас, только ленивый не умеет, да и F5 при случае не обламывает нажать. Я-то уж подумал, что кложа научилась серверный код хот-аплоадить, с нулевым даунтаймом сервера.


              1. seryh
                21.12.2016 09:47

                Ну тут сабж о фронтенд разработке а не о серверной, теоретически можно хот релоадить и на сервере, подняв lein repl в докер контейнере на проде, не занимался таким. По фронту, важный момент в ClojureScript hot-reload, в том что не теряется стейт, когда у вас большое SPA терять стейт по F5 при разработке, становится расточительной тратой времени.


    1. pawlo16
      21.12.2016 08:54

      «дает все тоже самое» — не могли бы Вы привести пример реализации Elm архитектуры на CS в подтверждение? Как на счёт WebGL c автоимплементацией шейдеров?


      1. seryh
        21.12.2016 09:35

        Непонятно что вы имеете ввиду под "реализации Elm архитектуры на CS", и как вы себе это представляете. Если вам интересно как на ClojureScript можно работать с WebGL то могли бы воспользоваться гуглом и поковырять исходных код библиотек по типу этой: https://github.com/alexkehayias/chocolatier, обычно там Interop в какую-либо популярную js библиотеку.


        1. pawlo16
          21.12.2016 10:06

          Под реализацией Elm архитектуры на CS я имею ввиду — внезапно! — приложение на CS, имеющее Elm архитектуру. Если Вам что-то непонятно из упомянутого, то мне не понятно, с чего Вы взяли, что CS дает тоже что и Elm, не понимая, что именно делает Elm.

          По поводу WebGL имею сообщить, что в Elm не просто встроена поддержка данной библиотеки, а компилятор умеет распарсить текст шейдеров на GLSL и проверить согласованность типов между шейдерами и основной программой. Из Вашего утверждения следует, что в CS есть функциональность с аналогичным API и именно её я ожидал увидеть


          1. seryh
            21.12.2016 10:17

            с чего Вы взяли, что CS дает тоже что и Elm,

            Внезапно прочитал статью выше, и ничего не увидел по WebGL.
            В любом случае WebGL не мой стек. Поэтому ничего по этому поводу сказать не могу.


            1. pawlo16
              21.12.2016 10:55

              Я вам привёл конкретный пример того, что есть в Elm и нет в CS. Вы же утверждаете, что CS даёт то же самое что Elm, никаких оговорок об ограниченности контекстом этого поста у Вас нет, у вас написано про основания выбора ЯП. Это как минимум не корректно


          1. seryh
            21.12.2016 10:23

            Из Вашего утверждения следует, что в CS есть функциональность с аналогичным API и именно её я ожидал увидеть

            В моем утверждении я через запятую перечислил что дает ClojureScript. В контексте статьи.


            1. pawlo16
              21.12.2016 10:47

              Слушайте, Elm Architecture в статье упомянута. Elm — он вообще весь про это, обсуждать его без понимания этого нет смысла. Вы же, как выяснилось, без понимания Elm Architecture, утверждаете что CS дает тоже что и Elm. Так вот это противоречит формальной логике.


              1. seryh
                21.12.2016 11:01
                +4

                Вы вообще понимаете что пишете? Вы хотите чтобы я изучил Elm, Elm Architecture, написал вам в этой архитектуре (WAT?) пример на ClojureScript в удобном вам стеке WebGL изучив его мимоходом, что бы вам что то доказать?


                Так вот это противоречит формальной логике.

                Вы на ходу меняете контекст моих доводов, додумываете что то сами, предполагаете что я могу читать ваши мысли. О какой логике может идти речь? Этот диалог потерял всякий смысл, и у меня нет никакого желания его продолжать.


                1. pawlo16
                  21.12.2016 11:15

                  Вы вообще понимаете что пишете?

                  да. А вы?

                  Вы хотите чтобы я изучил Elm, Elm Architecture, написал вам в этой архитектуре (WAT?) пример на ClojureScript в удобном вам стеке WebGL изучив его мимоходом, что бы вам что то доказать?


                  нет. Очевидно, вы не поняли моего вопроса. Сделайте ещё одну попытку, перечитайте внимательно мой вопрос в начале ветки, вдруг на сей раз получится понять.

                  Вы на ходу меняете контекст моих доводов, додумываете что то сами, предполагаете что я могу читать ваши мысли. О какой логике может идти речь?


                  О формальной. Я попросил Вас обосновать одно из Ваших утверждений, поскольку считаю его ложным.
                  image
                  «Доказывать обязан тот, кто утверждает, а не тот, кто отрицает.» Положение римского права.
                  Вы как выяснилось ничего обосновать не можете. Отсюда следует, что Вы спороли фигню, ляпнули не подумав.


                  1. seryh
                    21.12.2016 11:24
                    +2

                    Мое высказывание: "Из плюшек дает все тоже самое — функциональщина, иммутабельность, легкий рефакторинг, мощный REPL."


                    Вы вырезали из него "дает все тоже самое" придумали удобный вам контекст. И далее действовали как троль.


                    Вам достаточно таких обоснований?


                    1. pawlo16
                      21.12.2016 11:46
                      -5

                      придумали удобный вам контекст
                      это вы фантазируете сейчас. Вы хорошо понимаете смысл словосочетания «все тоже самое» в русском языке, или мне привести его здесь для Вас?
                      Вам достаточно таких обоснований?
                      Я несколько раз написал, что именно в Вашем случае является обоснованием. Вы, похоже, ни с первого, ни со второго раза не схватываете.


              1. motor4ik
                21.12.2016 13:22

                сила CS в том, что он дает вам свободу, и вы не зависите никак от авторов языка, и не ждете пока они запилят фичу, и не привязаны ни к каким архитектурам, надо elm architectire — пожалуйста re-frame, надо распарсить что-то при компиляции, или добавить функционал в язык, пожалуйста, напишите макрос или опять же воспользуйтесь библиотекой, вот допустим в elm есть debugger, я не ждал пока запилят такой же в CS, а написал его за пару дней re-frisk


                1. pawlo16
                  21.12.2016 14:44

                  А цена этой гибкости и свободы — отсутствие гарантий, которые даёт система типов Elm, надо писать очень много тестов. А большие энтерпрайзные приложения с глубокой иерархией модулей, каждый своим стейтом и набором сообщений — это как раз тот случай, где стат. типизация нереально спасает. И я не обнаружил, что Elm как-то сковывает, ограничивает и вынуждает пребывать в ожидании некой фичи. Тайп-классов разве что не хватает, но в их отсутствии есть и положительные стороны.

                  re-frame интересный, да. Могу ошибаться, похоже subscribe там имеет иную семантику нежели чем сейчас в Elm, что-то типа подписки на FRPшные сигналы.


  1. wheercool
    20.12.2016 18:45
    +3

    Честно говоря слабенькая статья получилось из разряда «Как я сходил в магазин». Ценность ее сомнительная, никаких технических деталей не увидел.
    Про плюсы Elm в статье упоминалось, а вот минусы никак не были освещены.
    Как по мне, самая большая проблема с Elm это даже не синтаксис, а интеграция с уже существующими библиотеками. Там как бы 2 пути: либо использовать порты (актуально для библиотек с сайд эффектами), либо переписывать на Elm (возможно есть еще какой-то путь). Было бы интересно если бы Вы как-то затронули эту темы.
    А так, спасибо за популяризацию ФП :)


    1. ad3w
      20.12.2016 20:11

      Или использовать purescript :)


      1. raveclassic
        21.12.2016 02:19

        Прекрасная вещь, кстати!


    1. dontpanic
      20.12.2016 21:25
      +1

      Спасибо за критику! Я действительно долгое время написанное держал в черновиках, но побудило к публикации именно малое наличие информации на данную тематику. До полноценной критики мне ещё надо получить опыта, поэтому и порты например не затронуты, потому что в своём приложении я использовал только Elm-библиотеки. И вы правы, для людей уже разбирающихся — ценность статьи невелика.


    1. pawlo16
      21.12.2016 11:27

      Минусы:

      • нет тайп-классов и do-нотации
      • асинхронность только через сообщения, нельзя сделать async/await


  1. j_wayne
    20.12.2016 23:26
    +1

    На мой взгляд, самый большой недостаток Elm — это elm-html.
    Он абсолютно недружелюбен ни к верстальщикам, ни к их привычным инструментам.
    Та же проблема, что с JSX, но несколько глубже.
    Я в курсе про причины, почему так сделано. Но от этого не легче. Мне не настолько нужна компиляция шаблонов с проверкой типов, а вот верстку делегировать выгодно.


    1. Dimchansky
      21.12.2016 15:00
      +1

      Да, есть такое. Но можно Html to Elm использовать, как переводчик для верстальщиков. :)


  1. pawlo16
    21.12.2016 08:45

    Проблем с разделением на модули нет вообще никаких — нужно (всего лишь!) сделать гомоморфизм из событий повторно использующего high-level в события повторно используемого чайлд-модуля. А не наоборот, как в статье по ссылке про translator pattern.

    module ChildModule exposing (Msg, view, update..) -- экпозить только сам тип событий, а все его конструкторы скрывать
    
    type Msg 
     = ...
    

    module Main 
    import ChildModule 
    
    type Msg 
     = ChildMsg Child.Msg
     | ..
    

    И далее в Main — Cmd.map ChildMsg… Sub.map ChildMsg… и т.п.


  1. OneFive
    21.12.2016 13:46

    Какая разница по скорости написания кода на Elm и react + redux? Хочу изучить Elm, но мне кажется он замедляет разработку, возможно так просто кажется из-за непонимания.


    1. dontpanic
      21.12.2016 13:50

      Для какого-то реального проекта я сейчас возьму react+redux, а не Elm, как раз из-за скорости написания и большого входного порога. Я изучаю Elm факультативно, на несложных примерах пока. Это интересно, но даже сейчас написать что-то большое мне будет сложней и дольше.


  1. mkpankov
    21.12.2016 15:12
    +2

    Тоже смотрел на Elm с прицелом на фронтенд-язык на будущие года 3-5. Понравилось:


    • Статические типы, хорошие сообщения об ошибках
    • Машинная проверка совместимости API по semver
    • Очень просто начать
    • Инструменты и поддержка редакторов. В Emacs завелось сразу всё (форматирование, автодополнение и т.д.), правда проверка кода на лету сделана своей штукой вместо flycheck и есть некоторые проблемы с интеграцией. elm-reactor тоже порадовал.
    • Простой код. 3 вещи, о которых надо заботиться, всё по MVC. Никаких компонентов, биндингов, корней модели и прочей чепухи, которую предлагают в том же Angular 2.
    • Подытоживая, "встроенный в язык фреймворк".

    Не понравилось:


    • В основном, делает 1 человек.
    • У проекта 1 хоть сколько-нибудь серьёзный коммерческий пользователь (NoRedInk). Там работает второй заметный участник проекта.
    • Никаких гарантий стабильности, не всегда работают инструменты апгрейда версии Elm. Deprecation warning'и делаются недели за 2 до выхода версии.
    • Релизы "от балды", нет чёткого расписания и процедура непрозрачна.
    • Процесс разработки новых версий в целом не особо прозрачен, непонятно чего ждать.
    • При условии использования императивного языка на бэкенде, impedance mismatch между фронтендом и бэкендом. Тяжело переключаться с одной парадигмы на другую, если ты "fullstack".
    • Практически невозможно нормально использовать готовые библиотеки на JavaScript.
    • Вёрстка через исходный код. Может быть сложно научить вертальщика и деплоить.
    • Встроенный в язык фреймворк. Может быть минусом, если хочется гибкости.

    Итого:
    Очень круто для энтузиастов, но переписывать мир на Elm без серьёзной поддержки коммерческих пользователей никто не будет, а она пока отсутствует.


    1. mkpankov
      21.12.2016 16:52

      И ещё пару пунктов вспомнил:


      • От стандартной библиотеки ощущение куцести. Например, есть родная поддержка Юникода, но например Core.Char с юникодными символами не работает — только с ASCII
      • Официальная документация местами хромает. Например, в Svg куча функций, к которым даже краткое описание не написано. Помимо этого, практически нигде нет примеров того, как пользоваться пакетами. Просто "вот вам API, делайте с ним что хотите".