В Elm мы создаем приложения, используя:
- Неизменные данные
- Чистые взгляды, которые описывают DOM
- Однонаправленный поток данных
- Централизованное государство
- Централизованное место, где описаны мутации данных
- Содержатся побочные эффекты
безопасности
Другая большая выгода вяза — безопасность, которую это обеспечивает. Полностью избегая возможности значения NULL, это заставляет нас обрабатывать все альтернативные пути в приложении.
Например, в JavaScript (и многих других языках) вы можете получить ошибки времени выполнения, выполнив что-то вроде:
var list = [] list[1] * 2
Это вернет NaN
в JavaScript, который вы должны обработать, чтобы избежать ошибки во время выполнения.
Если вы попробуете нечто подобное в Elm:
list = [] (List.head list) * 2
Компилятор отклонит это, сообщив, что List.head list
возвращает тип Maybe . Тип Maybe может содержать или не содержать значение, мы должны обработать случай, когда значением является Nothing
.
(Maybe.withDefault 1 (List.head list)) * 2
Это дает нам большую уверенность в наших приложениях. Очень редко можно увидеть ошибки времени выполнения в приложениях Elm.
Образец заявки
Чтобы получить более четкое представление о языке Elm и о том, как с его помощью создаются приложения, давайте разработаем крошечное приложение, которое отображает HTML-элемент, перемещающийся по странице. Вы можете попробовать это приложение, перейдя по адресу http://elm-lang.org/try и вставив туда код.
import Html import Html.Attributes exposing (style) import Time name : Html.Html name = Html.text "Hello" nameAtPosition : Int -> Html.Html nameAtPosition position = Html.div [ style [("margin-left", toString position ++ "px")] ] [ name ] clockSignal : Signal Float clockSignal = Time.fps 20 modelSignal : Signal Int modelSignal = Signal.foldp update 0 clockSignal update : Float -> Int -> Int update _ model = if model > 100 then 0 else model + 1 main : Signal Html.Html main = Signal.map nameAtPosition modelSignal
Давайте рассмотрим это по частям:
import Html import Html.Attributes exposing (style) import Time
Сначала мы импортируем модули, которые нам понадобятся в приложении.
name : Html.Html name = Html.text "Hello"
name
— это функция, которая возвращает Html
элемент, содержащий текст Hello
.
nameAtPosition : Int -> Html.Html nameAtPosition position = Html.div [ style [("margin-left", toString position ++ "px")] ] [ name ]
nameAtPosition
оборачивает name
в тег div
. Html.div
— это функция, которая возвращает элемент div
. Эта функция занимает целочисленную position
в качестве уникального параметра.
Первый параметр Html.div
— это список атрибутов HTML. Второй параметр — это список дочерних элементов HTML. Пустой тег div будет Html.div [] []
.
style [("margin-left", toString position ++ "px")]
создает атрибут стиля HTML, который содержит margin-left
с заданной позицией. Это закончится как style="margin-left: 11px;"
когда вызывается с позиции 11
.
Таким образом, в итоге nameAtPosition
отображает Hello
с полем слева.
clockSignal : Signal Float clockSignal = Time.fps 20
Здесь мы создаем сигнал, который передает сообщение 20 раз в секунду. Это сигнал поплавков. Мы будем использовать это как пульс для обновления анимации.
modelSignal : Signal Int modelSignal = Signal.foldp update 0 clockSignal
clockSignal
дает нам пульс, но сообщения, которые он посылает через сигнал, бесполезны, полезная нагрузка clockSignal
— это просто дельта между каждым сообщением.
Что нам действительно нужно, так это счетчик (то есть 1, 2, 3 и т. Д.). Для этого нам нужно сохранить состояние в нашем приложении. То есть возьмем последний отсчет, который у нас есть, и увеличиваем его каждый раз, clockSignal
срабатывает clockSignal
.
Signal.foldp
— это то, как вы сохраняете состояние в приложениях Elm. Вы можете думать о foldp
аналогично Array.prototype.reduce
в JavaScript, foldp
принимает функцию накопления , начальное значение и исходный сигнал .
Каждый раз, когда исходный сигнал передает событие, foldp
вызывает функцию накопления с предыдущим значением и сохраняет возвращаемое значение.
Таким образом, в этом случае, каждый раз, когда clockSignal
передает сообщение, наше приложение вызывает update
с последним счетом. 0
является начальным значением.
update : Float -> Int -> Int update _ model = if model > 100 then 0 else model + 1
update
является функцией накопления . В качестве первого параметра он принимает значение Float
являющееся дельтой из clockSignal
. Целое число, которое является предыдущим значением счетчика в качестве второго параметра. И возвращает другое целое число, которое является новым значением счетчика.
Если model
(предыдущее значение счетчика) больше 100, мы сбрасываем ее на 0, в противном случае просто увеличиваем на 1.
main : Signal Html.Html main = Signal.map nameAtPosition modelSignal
Наконец, каждое приложение в Elm начинается с main
функции. В этом случае мы map
modelSignal
мы создали выше, через функцию nameAtPosition
. То есть, каждый раз, когда modelSignal
передает значение, мы повторно визуализируем представление. nameAtPosition
будет получать полезную нагрузку от modelSignal
качестве первого параметра, эффективно изменяя стиль margin-left
элемента div двадцать раз в секунду, поэтому мы можем видеть текст, перемещающийся по странице.
Приложение, которое мы только что создали, демонстрирует:
- HTML в вязе
- Использование сигналов
- Сохраняя состояние функциональным образом
- Чистые взгляды
Если вы использовали Redux, вы заметите, что есть несколько параллелей между Elm и Redux. Например, update
в Elm очень похоже на редукторы в Redux. Это потому, что Redux был сильно вдохновлен архитектурой Elm .
Вывод
Elm — это захватывающий язык программирования, который включает в себя отличные шаблоны для создания надежных приложений. Он имеет краткий синтаксис, с большой встроенной безопасностью, которая позволяет избежать ошибок во время выполнения. У него также есть отличная система статических типов, которая очень помогает во время рефакторинга и не мешает, потому что использует вывод типов.
Кривая обучения тому, как структурировать приложение Elm, не тривиальна, поскольку приложения, использующие функциональное реактивное программирование, отличаются от того, к чему мы привыкли, но они того стоят.
Дополнительные ресурсы
- При создании больших приложений в Elm рекомендуется использовать архитектуру Elm. Смотрите этот учебник для получения дополнительной информации.
- Сообщество Elm Slack — отличное место, чтобы обратиться за помощью и советом.
- Видеоролики Pragmatic Studio о Elm — отличный ресурс для начала работы.
- Elm-tutorial — это руководство, над которым я работаю, чтобы научить создавать веб-приложения с помощью Elm.