автор: Dee Mon

Elm 0.17

Изначально Elm появился как воплощение идей FRP, и thesis (дипломную работу?) автора Elm'a я могу всем рекомендовать как замечательное изложение идей и разных подходов к FRP, плавно переходящее в объяснение исходной архитектуры Elm'а.

Там вся динамика была построена на идее сигналов: это когда есть обычные иммутабельные данные типов A,B,C... и есть отдельная категория типов A',B',C'... описывающих такие же данные, но меняющие свои значения со временем (навроде Time -> A'), и был функтор Signal из первой категории во вторую. Пишешь чистый код, работающий с простыми иммутальными данными, потом этим функтором лифтишь свои чистые функции в мир динамически меняющихся значений.

Есть набор внешних источников событий/данных, вроде координат мыши, т.е. уже живущих во второй категории, и нужно построить в ней функцию из нужных источников событий/данных в некое дерево контролов, элементов. А рантайм уже сам позаботится о том, чтобы все события и новые данные проходили как надо через все преобразования и получающееся на выходе дерево превращалось в DOM страницы.

Ну и были во второй категории специальные комбинаторы для обращения с временнЫми потоками данных, вроде соединения, сворачивания и пр.

 

Потом в Elm'е появились Mailbox'ы и Elm Architecture, в которой программа описывалась двумя функциями view и update, а также начальным значением пользователького типа Model (содержащего все данные)

update получала значение произвольного заданного пользователем типа Action (обычно это перечисление разных действий) и текущее значение модели, a возвращала она обновленное значение модели

view отображала значение модели в дерево элементов, принимая одним из параметров "адрес" (значение специального типа Mailbox). Возвращаемые view элементы в своих атрибутах могли иметь функции "что делать при нажатии/изменении", эти функции получали тот самый "адрес", чтобы слать свои оповещения туда

Так все оставалось иммутабельным и чистым, а рантайм заботился о доставке всех событий в форме пользовательского типа Action в функцию update. Так осуществлялся круговорот событий-реакций в колесе сансары. В явном виде сигналы уже не участвовали в Elm Architecture, но в разных API еще оставались.

 

В свежей версии 0.17 авторы сказали "прощай FRP" и выкинули все сигналы нафиг.

И API для построения дерева элементов поменяли заодно.

Зато добавили модный способ работы с первоклассными эффектами, как у взрослых. Осталась Elm Architecture, но уже другая. Теперь программа - это

         program : {  init          : (model, Cmd msg) 
                   ,  update        : msg -> model -> (model, Cmd msg) 
                   ,  subscriptions : model -> Sub msg
                   ,  view          : model -> Html msg
                   } -> Program Never

Т.е.

view отображает модель в DOM HTML, но тип дерева элементов параметризован твоим типом сообщений, ибо в атрибутах элементов вставляются функции реакций на события, которые производят значения этого самого типа твоих сообщений. Им теперь не нужно знать ни про какие мэйлбоксы, не нужно туда ничего слать, просто произвести сообщение, рантайм сам знает куда его доставлять.

Кроме того есть функция subscriptions, которая, исходя из текущего состояния модели, говорит, какие внешние события нам интересно слушать, и тип ее ответа тоже параметризован типом наших сообщений, т.к. внешние события приходят в рамках все того же потока сообщений, а когда подписываешься на внешнее событие, говоришь, как его завернуть в твой тип сообшений.

Ну и update, которая получает все эти сообщения твоего типа и меняет модель, а заодно может произвести "команду" - указание рантайму произвести некоторый эффект, подобно значению типа IO Smthng в хаскеле.

 

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

В версии 0.17 зачем-то изменился синтаксис описания модулей, там мелочь поменялась [exposing (..) вместо прежнего (..) where], но из-за нее некоторые библиотеки не собираются, надо одну строчку поменять в заголовке модуля.

 

Близкий к хаскелю синтаксис и отличный вывод типов в сочетании с referential transparency (от чистоты и иммутабельности) позволяют очень легко массировать код: любой кусок можно элементарно превратить в функцию, перенести в другое место, параметризовать, при этом не порождается бойлерплейта

В синтаксисе нет where, и это очень хорошо

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

Можно описывать свои операторы, но только на самом внешнем уровне, т.е. определить оператор локально (чтобы было замыкание) нельзя.

А еще там удобные record'ы с raw polymorphism'ом!

О чем еще мечтать? :)


comments

Я когда-то тоже фанатѣлъ отъ Elm. Но сейчасъ я недоволенъ, что убрали типъ сигналовъ. Стало все еще болѣе куцымъ, чѣмъ было раньше. Работа съ асинхронными процессами теперь будетъ еще болѣе ad hoc, чѣмъ раньше.

По-моему, когда нѣтъ явныхъ сигналовъ въ видѣ функтора, то работа программы становится совершенно загадочной и таинственной. Хотя рантаймъ возможно и сталъ болѣе эффективнымъ, потому что у программиста стало меньше функцiй, и рантаймъ можетъ больше оптимизировать.

Мне лично не кажется, что количество магии увеличилось. Как раньше под капотом был скрыт большой рантайм с хитрым графом, так и сейчас незаметный почтальон, доставляющий сообщения. При этом описание программы выглядит как красивая система урввнений, решил ее - и все складно работает. Освоить новую архитектуру и начать писать в ней, по-моему, проще чем с сигналами. Там, чтобы свою задачу в них выразить, много думать приходилось поначалу.

а чем отличается тайпкласс от type alias?

В основном тем, что неявно передается. type alias там просто record описывает, экземпляр ее надо явно в функцию передать.

ага, ну, в Агде тоже. собственно, тайпклассы хороши, когда по единственному экземпляру для каждого типа

По поводу отправления в пекло Signal - это огромный плюс. Как по мне, так комбинатор

   join : Signal (Signal a) -> Signal a 

очень слабый и сложный для понимания в сравнении с

   bind : (a -> View b) -> View a -> View b

к которому в итоге пришли

> Язык стал проще и одновременно удобнее.

Всё так. Но, с другой стороны, автор взял направление на отвязывание Elm от JS, типа, это всего лишь одна из платформ, в которую мы компилируемся. На основании этого в 0.17 выпилили все пользовательские нативные модули, и дальше интероп со средой выполнения предлагается делать исключительно через порты, команды и подписки.

В смысле выпилили? Вот я смотрю на пакет elm-linear-algebrals, там почти весь код имеет вид

inverseOrthonormal : Mat4 -> Mat4
inverseOrthonormal = Native.MJS.m4x4inverseOrthonormal

и все подобные упомянутые ф-ии из Native.MJS живут в соответствующем обычном .js файле.

Им самим можно (иначе как бы они webapi делали), а пользователям - уже нет. Раньше была подробная инфа как эти нативные модули писать и связывать с Elm, щас токо через javascript-as-service: http://guide.elm-lang.org/interop/javascript.html , а все остальное должно быть в elm-lang

Кул стори. Я влюбился в Elm где-то в районе 0.12 и довольно долго в него игрался, ходил на митапы, ездил Эвану по ушам про Эрланг и все такое. А как собрался-таки написать часть своего приложения на Эльме так сразу соскочил на Purescript: я начал писать компонент, сделал очень простенькую демку и показал ее одному из потенциальных кастомеров и он возвопил "О! Мечтаю о таком! Только в виде API пожалуйста: я туда REST`ом шлю, а ты мне эту красоту - в ответ." И тут дети поняли что принесли они из леса не язык программирования, а фигню какую-то. Потому что такая вот простая задача на Эльме не решается вообще от слова "совсем". И ушел я на пюрескрипт, и скучаю по вменяемым сообщениям об ошибках и скорости компилятора. Но с тех пор я уже пописал на пюрескрипте и для node (вот ту самую красоту) и для Google Apps. И ничего из этого на Эльме я бы сделать не смог.

Просто люди как-то не сразу понимают что эльм - это не язык, это вебфреймворк. И когда они спрашивают "а можно ли на эльме написать ....", это звучит примерно как "а можно ли на Angular написать компилятор С".

А можно хотя бы в двух словах про случившийся с Эльмом затык? Какие-то выбранные автором языка идиомы оказалось на сервер не ввернуть или что?

В отличие от Purescript, Эльм таскает с собой довольно внушительный рантайм и этот рантайм рассчитан на жизнь внутри броузера. Можно конечно сделать обертку, которая будет имитировать жизнь в броузере для nodejs, но при наличии жесткого ограничения в Эльме на нативные модули это становится довольно затейливым приключением. Эльм - это клиентский фреймворк, все просто. Загонять его на сервер можно, но как бы это сказать...