Как я встретил вяз

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

Я случайно наткнулся на язык вязов. Это было на Strange Loop 2015, где я впервые увидел, как Ричард Фельдман и его команда продемонстрировали фронтенд-работу, которую они проделывали с Elm. Код выглядел просто и элегантно, и больше всего казалось, что он решает проблему состояния, от которой страдает так много интерфейсных проектов.

Управление состоянием в JavaScript, как известно, сложно. Состояние хранится во всей модели DOM и управляется кодом JavaScript. Это означает, что одна часть одной и той же кодовой базы может манипулировать состоянием способами, которые могут противоречить тому, как другие части кодовой базы используют то же состояние. В результате код становится хрупким, и его трудно поддерживать. Фреймворки JavaScript пытались решить эту проблему, сохраняя состояние в коде JavaScript и отображая HTML на основе этого состояния. С одной стороны, это помогло решить проблемы, но с другой стороны, добавило еще один уровень сложности во интерфейсные проекты. Иногда просто поддержание среды сборки для современной кодовой базы JavaScript может приблизиться к сложности реального кода.

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

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

Вяз Архитектура

Архитектура Elm состоит из трех основных компонентов.

Модель

Модель - это место, где хранится все состояние вашего приложения. Главное помнить, что это состояние неизменяемо. Невозможно изменить какую-либо отдельную часть состояния. Все изменения приводят к созданию полностью новой копии модели. Это состояние также можно изменить только одним способом (с помощью функции обновления).

Вот простой пример модели и функции, которая инициализирует модель.

type alias Model =
  {
    xCoord: Int
  , yCoord: Int
  }
init : Model
init = 
  { xCoord = 0
  , yCoord = 0
  }

Вид

Представление - это функция, которая принимает модель и создает HTML. Для меня это был момент эврики изучения Вяза. Превращение создания HTML в чистую функцию значительно упрощает тестирование и отладку приложений. Мы знаем, что никакой побочный эффект не может повлиять на обработанный HTML. Любые изменения должны быть связаны с изменением модели.

Вот пример представления, в котором используется модель, определенная выше. Он просто отображает координаты x / y, хранящиеся в модели, и предоставляет четыре кнопки, которые можно использовать для изменения модели.

view : Model -> Html Msg
view model =
  div []
    [
      div [] [ text (toString model.xCoord) ]
    , div [] [ text (toString model.yCoord) ]
    , button [ onChange Up ] [ text "Up" ]
    , button [ onChange Down ] [ text "Down" ]
    , button [ onChange Left ] [ text "Left" ]
    , button [ onChange Right ] [ text "Right" ]
    ]

Обновлять

Функция обновления - единственный способ изменить модель. Модель принимает сообщение и модель и создает новую модель. Сообщение - это тип, который определяет действие, которое может быть применено к модели.

Вот пример, в котором используются модель и представление, определенные выше. Обратите внимание, что типы сообщений отображаются непосредственно в параметрах атрибутов onChange в представлении. Последовательность выполнения программы выглядит так:

  • Нажатие кнопки вызывает выполнение функции обновления с определенным сообщением.
  • Функция обновления создает новую версию модели.
  • Новая модель приводит к повторной визуализации представления с использованием нового значения модели.
type Msg 
  = Up
  | Down
  | Left
  | Right
update : Msg -> Model -> Model
update msg model =
  case msg of 
    Up -> 
      { model | yCoord = model.yCoord + 1 }
Down-> 
      { model | yCoord = model.yCoord - 1 }
Left-> 
      { model | xCoord = model.yCoord - 1 }
Right-> 
      { model | yCoord = model.yCoord + 1 }

Собираем все вместе

С этими компонентами пора создавать приложение верхнего уровня. Это делается с помощью функции App.program, включенной в ядро ​​Elm. Получающееся в результате приложение верхнего уровня очень просто.

Мы просто передаем три функции, которые мы создали, в соответствующие параметры beginnerProgram, и все готово. У нас есть полноценное веб-приложение, написанное на Elm.

main : Program Never
main =  
  App.beginnerProgram 
   { init= init
   , view = view    
   , update = update    
   }

Мои выводы

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

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

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

  • Могу ли я подумать о том, как лучше использовать состояние в моем коде Ruby?
  • Стоит ли мне подумать о том, чтобы заняться бэкендом в Elixir?
  • Будет ли теперь легче изучать Haskell, когда Эльм избавил меня от страха перед функциональным программированием?

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