раздел 27
Gleam на практике
Gleam, минималистичный типизированный язык на BEAM. Первая половина, от первого gleam new до Wisp, Lustre и Telegram-бота: ADT, pattern matching, парсинг через Decoder, OTP-акторы, и тот же код в Erlang и в JavaScript. Вторая половина, серия по функциональному DDD: Value Object, Aggregate, паттерн Decider, event sourcing, CQRS и saga.
Начать раздел
Хочу пройти
Не получилось изменить список. Попробуй ещё раз.
Раздел открыт для всех. Войди, чтобы отмечать пройденные уроки, вести заметки и решать задачи в редакторе. войти
Уроки раздела
- Введение в Gleam, язык на BEAM
- Начало работы: gleam new, базовые типы, первый проект
- Функции, пайплайны и замыкания
- Типы данных и коллекции: ADT, pattern matching, List, Dict
- Рекурсия, свёртки и обработка ошибок
- Строки, BitArray и стандартная библиотека
- Type Safety и Parse Don't Validate
- Erlang FFI и системное программирование
- JavaScript FFI и фронтенд интеграция
- Процессы и OTP: акторы, supervisor, let it crash
- Тестирование на Gleam: gleeunit, property-based, snapshot
- Веб-разработка с Wisp
- Фронтенд с Lustre: TEA-архитектура в браузере
- Заключение и следующие шаги
- Telegram-бот с Telega 3: роутер, диалоги, flow, эксплуатация
- Functional DDD на Gleam: с чего начинаем
- Value Object и Email: opaque + smart-конструктор
- Money: валюта как phantom-параметр
- Entity и Customer: identity против equality
- Aggregate Reservation: стек инвариантов через result.try
- Domain Events: команды и события как ADT
- Repository как порт: in-memory backend для тестов
- Bounded Context: один Customer, два несовместимых типа
- Composition root на Wisp: где собирается граф зависимостей
- SQLite-репозиторий через sqlight: реальный adapter под тот же порт
- Конфиг и build_repo: финальная сборка первого блока
- Event Modeling: от свимлейна на доске к типам Gleam
- Decider Жереми Шассена: три функции вместо агрегата
- Event Sourcing: хранить факты, а не состояние
- CQRS: разделить запись и чтение, проекция как свёртка
- Process Manager и Saga: charge-on-checkout с компенсацией
- Aggregateless ES: единый стрим и Facts вместо агрегата
- Кейс Decider: Uno как FSM с ходом и направлением
- Континуации: одна бизнес-логика, любые эффекты
Понятия курса
Понятия разобраны внутри уроков. Нажми на понятие, чтобы открыть урок, где оно объясняется.
Старт
- Введение, Gleam и BEAMЗачем ещё один ФП-язык, что такое BEAM, чем Gleam отличается от Elixir, Haskell и Elm. Двойной таргет: Erlang и JavaScript.урок 1 · Введение в Gleam, язык на BEAMопираются: урок 2
- Функции, пайплайны и замыканияPipeline `|>`, замыкания, именованные (labelled) аргументы, частичное применение. Это слой, где Gleam ощущается как чистый язык функций.урок 3 · Функции, пайплайны и замыканияопираются: урок 4
- gleam new и базовые типыУстановка, структура проекта, базовые типы: Int, Float, String, Bool. Особенности арифметики Float (точка-операторы).урок 2 · Начало работы: gleam new, базовые типы, первый проектопираются: урок 3
Типы и данные
- Рекурсия, свёртки, Result и OptionРекурсия с хвостовой оптимизацией, fold как обобщение, ошибки как значения: Result, Option, use + result.try, Railway-Oriented Programming.урок 5 · Рекурсия, свёртки и обработка ошибокопираются: урок 6, урок 7, урок 11, урок 34
- Строки, BitArray и стандартная библиотекаСтроки как UTF-8, BitArray для битовых протоколов, обзор stdlib: string, list, dict, io, int, float.урок 6 · Строки, BitArray и стандартная библиотека
- ADT, pattern matching, List и DictАлгебраические типы данных, custom types, pattern matching через case, кортежи, records, List, Dict. Невозможные состояния невыразимы.урок 4 · Типы данных и коллекции: ADT, pattern matching, List, Dictопираются: урок 5
Дисциплина типов
- Parse, don't validate. Opaque, phantom, decoderПарсим, а не валидируем. Opaque-типы со smart-конструкторами, phantom-типы для невидимых ограничений, JSON через gleam/dynamic/decode.урок 7 · Type Safety и Parse Don't Validateопираются: урок 8, урок 9, урок 12, урок 17
BEAM и FFI
- JavaScript FFI и target = javascriptТот же Gleam-код собирается в JS-модуль. Подключение JS через FFI, обёртка fetch, минимальные хелперы DOM.урок 9 · JavaScript FFI и фронтенд интеграцияопираются: урок 13, урок 34
- Erlang FFI: @external, atoms, ETSВызов Erlang-функций через @external, работа с atoms, ETS-таблицы как in-memory KV-стор, низкоуровневое spawn/send/self.урок 8 · Erlang FFI и системное программированиеопираются: урок 10
- OTP, акторы, supervisor, let it crashgleam/otp/actor как типобезопасная обёртка над BEAM-процессами. Subject и Selector. Запрос-ответ через actor.call. Supervisor и три стратегии перезапуска.урок 10 · Процессы и OTP: акторы, supervisor, let it crashопираются: урок 12, урок 15
Практика
- Куда дальше: экосистема BEAM и HexРаспределённые системы на BEAM, Phoenix-связка через Elixir, Hex-пакеты, профилирование через :observer.урок 14 · Заключение и следующие шаги
- Тестирование: gleeunit, qcheck, birdiegleeunit для юнитов, qcheck для property-based проверок, birdie для snapshot. Те же идеи, что в RTL + Vitest, разный сахар.урок 11 · Тестирование на Gleam: gleeunit, property-based, snapshot
- Веб на Wisp: запрос-ответ, middleware, JSONWisp, веб-фреймворк на функциях. Роутер как case, middleware как обёртки, JSON через gleam_json, http-сервер на Mist внутри supervisor-дерева.урок 12 · Веб-разработка с Wispопираются: урок 15
- Lustre: TEA-архитектура для браузераThe Elm Architecture в Gleam: model, msg, update, чистый view. Никаких хуков, side-effects через Effect и Subscription.урок 13 · Фронтенд с Lustre: TEA-архитектура в браузереопираются: урок 14
- Telegram-бот на Telega: команды, session, flowФинальный проект серии. Команды, роутер, middleware, Session, Conversation API (wait_*), Flow API как конечные автоматы с историей.урок 15 · Telegram-бот с Telega 3: роутер, диалоги, flow, эксплуатацияопираются: урок 16
Functional DDD
- Кейс Decider: Uno как FSM с историейПараллельный мини-домен. Тот же Decider, что в 28, но карты, ход, направление, штрафы. BDD-сценарии по `gleamuno` и `Papipo/uno`.урок 33 · Кейс Decider: Uno как FSM с ходом и направлением
- Конфиг и build_repo: финальная сборкаКат 10. envoy для env-переменных, фабрика `build_repo(config)` возвращает нужный adapter. На этом первая половина серии закрывается.урок 26 · Конфиг и build_repo: финальная сборка первого блокаопираются: урок 27
- Entity: Customer, идентичность против равенстваКат 3. Customer как entity с устойчивым `CustomerId`. Два клиента равны по id, не по полям. `update_email` возвращает новую запись.урок 19 · Entity и Customer: identity против equalityопираются: урок 20
- Event Modeling: воркшоп от свимлейна к кодуАдам Дымитрук. Команды (синие), события (оранжевые), read-models (зелёные), UI (жёлтые). Свимлейн на доске, потом перенос в типы.урок 27 · Event Modeling: от свимлейна на доске к типам Gleamопираются: урок 28
- Functional DDD на Gleam: с чего начинаемЗачем серия по функциональному DDD, что заберём через 18 уроков, какой сквозной домен (Hotel Booking + Uno), три тезиса North Star.урок 16 · Functional DDD на Gleam: с чего начинаемопираются: урок 17
- Money: валюта как phantom-параметрКат 2. Money(currency) с phantom-параметром, сложение возможно только в одной валюте, конвертация через FX-rate, `use <-` как guard.урок 18 · Money: валюта как phantom-параметропираются: урок 19
- Value Object и Email через opaque + smart constructorКат 1 escherize. Email как opaque-тип, конструктор только через `Email.parse`, валидация на границе, дальше система работает с типом.урок 17 · Value Object и Email: opaque + smart-конструкторопираются: урок 18
- Aggregate: Reservation, FSM и стек инвариантовКат 4. Reservation это агрегат: ADT состояний (NotPlaced, Reserved, CheckedIn, CheckedOut, Cancelled). Переходы возвращают Result, невозможные состояния не выражаются в типе.урок 20 · Aggregate Reservation: стек инвариантов через result.tryопираются: урок 21
- Bounded Context: разделение на модули и словарьКат 7. Один и тот же `Customer` в Reservations это не то же, что `Customer` в Billing. Каждый context, свой модуль, свой словарь.урок 23 · Bounded Context: один Customer, два несовместимых типаопираются: урок 24
- Domain Events: команды и события ReservationКат 5. Команда и событие это два разных типа. `decide(state, command) -> Result(List(Event), Error)`. Event это факт Reservation, который уже произошёл.урок 21 · Domain Events: команды и события как ADTопираются: урок 22
- Repository как порт: OrderRepo и in-memory backendКат 6. Repository это тип-запись с функциями `load` и `save`, домен не знает про базу. Сначала in-memory реализация на dict для тестов.урок 22 · Repository как порт: in-memory backend для тестовопираются: урок 23
- Composition root на Wisp: где собирается граф зависимостейКат 8. Wisp-приложение принимает Repo как параметр, корень собирает реальные функции в одном месте. HTTP-роутер тонкий, домен чистый.урок 24 · Composition root на Wisp: где собирается граф зависимостейопираются: урок 25
- CQRS: разделение write- и read-сторонКоманды идут через Decider, запросы, через отдельные read-models. Projection подписывается на стрим и материализует state для UI.урок 30 · CQRS: разделить запись и чтение, проекция как свёрткаопираются: урок 31
- Decider Жереми Шассена: универсальная единица домена`Decider(command, state, event, error)` плюс `decide`, `evolve`, `initial_state`. Из трёх функций собирается агрегат, FSM, процесс-менеджер.урок 28 · Decider Жереми Шассена: три функции вместо агрегатаопираются: урок 29
- Event Sourcing: стрим событий, replay, снапшотыСостояние не таблица, а свёртка событий. `fold(events, initial_state, evolve)`. Snapshot, ускорение replay. Optimistic concurrency через expected_version.урок 29 · Event Sourcing: хранить факты, а не состояниеопираются: урок 30
- Process Manager и Saga: charge-on-checkoutLong-running сценарий через границу контекстов: Front Desk checks out, Billing получает событие, выставляет счёт, ошибка, компенсация.урок 31 · Process Manager и Saga: charge-on-checkout с компенсациейопираются: урок 32
- SQLite-репозиторий через sqlight: реальный adapterКат 9. Schema, миграции, encode/decode для Order, транзакции. Repo меняется на SQLite, домен и тесты не меняются ни на строчку.урок 25 · SQLite-репозиторий через sqlight: реальный adapter под тот же портопираются: урок 26
- Aggregateless ES: Facts вместо агрегатовgleavent_sourced, единый стрим, Fact-функции, command-handlers без агрегатных границ. Когда транзакционная граница искажает модель.урок 32 · Aggregateless ES: единый стрим и Facts вместо агрегатаопираются: урок 33
Эффекты
- Континуации: одна логика, любые эффектыТип fn(fn(a) -> t) -> t, модуль return/then/each, одна task и три раннера: чистый, Result, Promise. Филинский 1994.урок 34 · Континуации: одна бизнес-логика, любые эффекты