Привет, Хабр!
Данный обзор познакомит вас с частичкой мира функциональных языков программирования, а именно с Elm, кто-то узнает про этот инструмент, кто-то про то, что фронтенд – это не только HTML, CSS и JS.

Elm – это функциональный язык программирования, который компилируется во всеми любимый javascript. Он обладает всеми плюсами и минусами этого семейства языков. Из самых важных особенностей стоит отметить, что язык является компилируемым. А также свойства языка принуждают вас сразу обдумать архитектуру вашего приложения: расписать, что попадёт в конкретную функцию, что из неё выйдет, и куда пойдёт дальше.
В данном материале я не буду углубляться в синтаксис языка, для этих целей существует множество гайдов (пусть и не так много, как в мейнстрим-языках).
Начну сразу с архитектуры приложений на Elm, которая показана на картинке ниже:

Elm генерирует HTML для отображения на экране, а затем приложение отправляет обратно сообщения (Msg) о каком-либо событии (например, нажатие кнопки).
Любое SPA (single page application) на Elm состоит из следующих элементов:
Init – начальное состояние модели.
Model — текущее состояние проекта.
View — отображение model.
Update — обновление состояния проекта на основе сообщений (Msg).
Что мы будем делать?
Для того что бы разобраться как работает Elm, было реализовано великое и могучее SPA под названием To-Do List (список дел) с возможностью записи дел и проведения некоторых операций над ними. Для демонстрации работы с запросами было добавлено соединение с БД (базой данных).
Наше SPA To-Do List будет обладать следующими возможностями:
· создание элемента To-Do List’а;
· редактирование (изменение названия и статуса выполнения задачи);
· удаление;
· сохранение в БД списка дел.
А реализовывать всё это мы будем с помощью следующих технологий:
1. Elm – виновник торжества.
2. Json-server и db.json в роли базы данных (БД).
3. Bootstrap – будет отвечать за красоту.
Начинаем с начала
Для того что бы использовать Elm, как бы это странно не звучало, необходимо установить Elm. Лучше скачивать с официального сайта (elm-lang.org). Так же на нём находится самая актуальная документация языка, которая по своей структуре напоминает обучающий курс. Можно скачать и с pypi, но пакет там не обновлялся с конца 2018 года.
После того как Elm установился, необходимо перейти в директорию проекта и запустить его с помощью команды:
elm reactor
Проект запустится на localhost:8000 и всё, достаточно изредка нажимать ctrl+s для сохранения и проект будет автоматически обновляться, не нужен даже LiveServer.
Архитектура приложения
Приложение состоит из 3-х модулей, каждый из которых вынесен в отдельный файл структура проекта показана на изображении ниже.

-
Main – основной модуль приложения, в котором происходит его инициализация, добавление в проект теги html, body и всех статичных элементов, а также вызов других модулей. Код данного модуля находится в файле с логичным названием Main.elm.
Main : Program Value Model Msg Main = Browser.document { init = =init, , view = view , update = update , subscriptions = subscriptions } type Msg = NoOp | TodosMsg TodosMsg.Msg type alias Model = {todos : List Todo, todoEditView : TodoEditView} init : flags -> ( Model, Cmd Msg ) init fs = let model = Model [] None cmds = Cmd.batch[ Cmd.map TodosMsg Todos.Models.fetchAll ] in ( model, cmds) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of let ( newTodoEditView, newTodos, cmd ) = Todos.Models.update subMsg mod-el.todoEditView model.todos newModel = { model | todoEditView = newTodoEditView, todos = newTodos } in ( newModel, Cmd.map TodosMsg cmd ) view : Model -> Document Msg view model = { title = "Туду" , body = [ div [] [ Html.map TodosMsg <| Todos.Models.viewEdit model.todoEditView , br [] [] , Html.map TodosMsg <| Todos.Models.viewList model.todos ] ] }
-
Models – модуль приложения, который отвечает за список дел. Из-за того, что пример небольшой, всё хранилось в одном месте. Но при более серьёзном проекте, хорошим тоном будет разделить код на разные модули. Здесь лежит таблица, которая хранит в себе дела, а также осуществлялись все действия с ними. Ниже приведена часть кода из данного модуля, который лежит в директории Todos и называется Models.elm.
-- Корпус таблицы viewList : List Todo -> Html Msg viewList todos = Table.table{ options = [ Table.striped ] , thead = Table.thead [] [ Table.tr [] [ Table.th [] [ text "Название" ] , Table.th [] [ text "Готово" ] , Table.th [] [ div [][ text "Действия" ] , div[][ delCompl ] ] ] ] , tbody = Table.tbody [] ( List.map todoRow todos ) } -- Строки таблицы todoRow : Todo -> Table.Row Msg todoRow t = let { id, title, completed } = table ( completedText, buttonText, buttonMsg ) = if completed then ("Да", "Невыполненно", Revert ) else ("Нет", "Выполненно", Complete) in Table.tr [] [ Table.td [] [ text title ] , Table.td [] [ text completedText ] , Table.td [] [ Button [ onClick <| buttonMsg t ][ text buttonText ] , Button [ onClick <| ShowEditView <| Editing t ][ text "Редактиро-вать" ] , Button [ onClick <| Delete t ][ text "Удалить" ] ] -- Функция удаления delCompl : Html Msg delCompl = Button[ onClick DeleteCompleted ][ text "Удалить выполненные" ]
-
Utils – модуль связи с БД. Здесь находится всё, что связано с данными, а именно: кодирование и декодирование json’а и запросы, для выполнения которых использовался пакет Http.request. Пример запроса показан ниже. Код вынесен из модуля Utils.elm.
--запрос для удаления delete : a -> String -> Platform.Task Http.Error a delete a url = let decoder = Json.Decode.succeed a request = Http.request {method = "DELETE" , headers = [] , url = url , body = Http.emptyBody , expect = Http.expectJsopn decoder , timeout = Maybe.Nothing , withCredentials = False } in Http.toTask request
После реализации всех указанных модулей получится следующий результат:

Для тестового проекта, который никто не увидит – сойдёт. Но я делаю его для души, поэтому просто необходимо добавить немного красоты. И Elm в этом поможет, с помощью тесной интеграции с Bootstrap. Подключаем его следующей строкой в main:
import Bootstrap.CDN as CDN
А также импортируем bootstrap классы добавляя элиасы для простоты вызова:
import Bootstrap.Button as Button # и т.д. по необходимости
Благодаря bootstrap’у и небольшой доработке кода получается следующая картина:

При изменении названия, кнопка «Новая задача» заменяется на поля для редактирования наименования дела.

База данных
Для подъёма базы данных я использовал json-server, который был запущен на 4000 порту. Всего БД имеет 3 поля для каждого дела, а именно:
title <str> - название дела;
completed <boolean> - готовность дела;
id <int> - идентификатор дела.
Команда для запуска следующая:
Json-server db.json -p 4000
В итоге база данных выглядит следующим образом:

Что касается ответа на вопрос:
«Elm — забава или серьёзный инструмент?», он очень прост — всё зависит от вашего проекта. От себя скажу, что изначально программирование на Elm было похоже на поход в магазин на руках. Из-за того, что всё было непривычно, Elm требовал на разработку часы, когда с javascript на то же самое хватило бы и нескольких минут. Данное неудобство связано скорее с изначальным обучением программированию в императивном стиле и обычной привычкой.
Elm, как и множество функциональных языков программирования ломает ваш мозг, если раньше вы никогда не программировали в функциональном стиле, но взамен, он структурирует ваши мысли. Учит не создавать множество лишних переменных и функций, а также продумывать архитектуру приложения до того, как вы садитесь за написание кода.
Из минусов так же стоит отметить нераспространённость этого языка, и как следствие — отсутствие актуальной документации и примеров на русском языке, иногда даже на английском. Не всегда получается сразу найти информацию и приходится копать в дебри форумов.
Полный код приложения доступен по ссылке: github.com/MikhailOznobikhin/ELM_todo
А пробовали ли вы писать код в функциональной парадигме, делитесь в комментариях?
ссылка на оригинал статьи https://habr.com/ru/post/696718/
Добавить комментарий