У меня двадцать лет в IT. Большую часть этого времени я проектировал и эксплуатировал инфраструктуру на PostgreSQL. Сейчас работаю архитектором: Go, Python, Postgres, Redis, ClickHouse, мониторинг на десятки тысяч баз. До этого писал на Ruby, пробовал Rust. Классический бэкенд-инженер со всеми вытекающими привычками: императивный код, мутабельное состояние, постоянные if err != nil { return err }.

А потом я начал писать бэкенд на Gleam — молодом функциональном языке на BEAM (Erlang VM), который появился в стабильной версии только в 2024 году. Навык ещё в разработке, но бэкенд уже работает, и я не жалею. Путь был... познавательным.

Эта статья — не туториал и не рекламный буклет. Это честный рассказ о том, почему я выбрал Gleam, какие шишки набил, что мне понравилось настолько, что я не хочу возвращаться, и что до сих пор бесит.

Что я делаю?

Разрабатываю голосовую игру для Яндекс Алисы. Ребёнок управляет кафе: принимает заказы, готовит блюда, обслуживает посетителей. Звучит просто, но под капотом:

  • HTTP-сервер, обрабатывающий запросы от платформы Алисы (JSON-протокол со своей спецификой)

  • Конечный автомат (FSM) с десятками состояний и переходов между сценами

  • Хранение состояния сессий в Redis (Алиса — stateless, состояние нужно передавать или хранить самому)

  • HMAC-SHA256 подпись состояния для защиты от подделки

  • Админ-панель на React для управления диалоговыми сценариями через API

  • Озвучка через ElevenLabs, звуковые эффекты, SSML-разметка

Стек: Gleam + Wisp (HTTP) + Mist (сервер) + Valkyrie (Redis) + Nginx + systemd + Ubuntu. Деплой — обычный Linux-сервер, без Docker и Kubernetes.

Почему Gleam, а не [ваш любимый язык]

Почему не Go

Go — основной язык в моём стеке, на нём написаны системы, которые я проектирую и поддерживаю. Для инфраструктурных сервисов он прекрасен: быстрая компиляция, горутины, один бинарник. Но для моделирования предметной области Go — страдание.

В Go нет алгебраических типов данных. Нет pattern matching. Обработка ошибок — бесконечные if err != nil. Когда ты моделируешь конечный автомат с двадцатью состояниями, где каждый переход должен быть валидным, Go превращается в минное поле: ты постоянно боишься забыть проверку, пропустить кейс, оставить невалидное состояние.

Почему не Rust

Rust я изучал перед Gleam и даже написал на нём прототип. Rust великолепен для системного программирования, но для HTTP-бэкенда с Redis — избыточен. Borrow checker решает проблемы, которых у меня нет: я не управляю памятью вручную, не пишу драйверы. А вот когнитивная нагрузка от сложности языка — это цена, которую я плачу за ненужные мне гарантии.

Почему не Elixir

Elixir — ближайший родственник Gleam, тоже на BEAM. Отличный язык. Но: динамическая типизация. Для небольшого-проекта, где я не могу позволить себе роскошь тестового отдела и CI с тысячей тестов, статические типы — это мой страховой полис. Gleam ловит целые классы ошибок на этапе компиляции, которые в Elixir всплывают только в runtime.

Почему Gleam

Gleam попал в пересечение всех моих требований:

  • Статическая типизация с алгебраическими типами и pattern matching — для моделирования FSM(конечного автомата).

  • BEAM под капотом — конкурентность, отказоустойчивость, горячая перезагрузка (теоретически).

  • Простой синтаксис — после Rust - это как задержать дыхание на пару минут и потом выдохнуть. Все предельно просто, никаких макросов, минимум магии.

  • Result-типы вместо исключений — каждая ошибка явная, каждый путь обработан.

  • JavaScript-интероп — для тех случаев, когда нужной библиотеки на Gleam нет (а таких случаев много).

Конечный автомат на типах - главная победа

Конечный автомат — сердце голосового навыка. У игры есть сцены (главное меню, кухня, зал, мини-игра), и в каждой сцене — свои допустимые переходы. Игрок не может «готовить», находясь в главном меню, и не может «принять заказ» на кухне.

В Go или Python это обычно делается через строки или enum-ы с runtime-проверками:

// Go — runtime error, если забыл проверку
if state == "kitchen" && action == "take_order" {
    return errors.New("invalid action for kitchen")
}

В Gleam это моделируется на уровне типов:

// Gleam — невалидный переход не компилируется
pub type Scene {
  MainMenu
  Kitchen(KitchenState)
  Hall(HallState)
  MiniGame(MiniGameType)
}

pub type KitchenState {
  Idle
  Cooking(dish: Dish, time_left: Int)
  Done(dish: Dish)
}

pub fn handle_kitchen(state: KitchenState, input: UserInput) -> Result(Scene, UserError) {
  case state, input {
    Idle, Cook(dish) -> Ok(Kitchen(Cooking(dish, dish.cook_time)))
    Cooking(_, ), _  -> Error(StillCooking)
    Done(dish), Serve -> Ok(Hall(Serving(dish)))
    _,_  -> Error(InvalidAction)
  }
}

Красота в том, что компилятор заставляет меня обработать все комбинации состояний и действий. Если я добавлю новую сцену и забуду написать обработчик — код не скомпилируется. В Go или Python это была бы ошибка, которая проявилась бы только, когда реальный пользователь попал бы в необработанную ветку.

Typed Errors: почему UserError лучше, чем panic

В начале проекта я обрабатывал ошибки как в Go — возвращал строки. Потом рефакторнул на типизированные ошибки, и это изменило всё.

pub type UserError {
  SessionExpired
  InvalidState(details: String)
  RedisConnectionFailed(reason: String)
  SceneNotFound(scene_id: String)
  HmacVerificationFailed
}

Теперь каждая функция явно декларирует, что может пойти не так:

pub fn load_session(session_id: String) -> Result(Session, UserError) {
  use raw <- result.try(
    redis.get(session_id)
    |> result.map_error(fn(e) { RedisConnectionFailed(e.message) })
  )
  use session <- result.try(
    decode_session(raw)
    |> result.map_error(fn(_) { InvalidState("corrupted session data") })
  )
  verify_hmac(session)
  |> result.map_error(fn(_) { HmacVerificationFailed })
}

Оператор use в Gleam — это то, что делает цепочки Result-ов читаемыми. Без него код был бы лестницей из case ... { Ok() -> ... Error() -> ... }. С ним — линейный поток, где каждый шаг может упасть, и ты точно знаешь, как именно.

Экосистема: где больно

Не буду врать: экосистема Gleam — это одновременно и самая сильная проблема, и причина, по которой ты можешь стать контрибьютором чего-то важного.

Redis: путь от radish к valkyrie

Первый клиент Redis, который я попробовал — radish. Работал, но с ограничениями. Потом перешёл на valkyrie — более зрелый, лучше API. Но даже valkyrie не покрывает все команды Redis. Несколько раз пришлось лезть в исходники и дописывать.

Для сравнения: в Go я бы взял go-redis и не думал. В Gleam выбор Redis-клиента — это исследовательский проект на пару вечеров.

HTTP: Wisp и его особенности

Wisp — HTTP-фреймворк для Gleam. Работает поверх Mist (HTTP-сервер на BEAM). В целом — приятный, минималистичный. Но есть неочевидный момент: Wisp использует String-based API, а не StringTree (StringBuilder). Я потратил час на отладку, потому что пытался передать StringTree туда, где ожидался String. Компилятор подсказал, но не сразу было ясно, почему типы не совпадают.

Библиотеки: чего не хватает

На момент написания статьи мне не хватало:

  • Полноценного HMAC-SHA256 (пришлось делать через Erlang FFI).

  • Продвинутой работы с JSON (gleam_json базовый, для сложных вложенных структур неудобен).

  • Хорошего логгера с уровнями и структурированным выводом.

  • ORM или хотя бы query builder.

Это цена раннего использования нового языка. Но это и возможность: каждая библиотека, которую ты напишешь, получает очень высокий шанс стать БИБЛИОТЕКОЙ в экосистеме.

Что мне понравилось настолько, что я не хочу возвращаться к другим языкам

Exhaustive pattern matching

Когда я добавляю новый вариант в тип — например, новую сцену в игре — компилятор показывает все места, где я забыл его обработать. Не один, не два — все. В Go я бы узнал об этом от пользователя в production.

Pipe-оператор

Оператор |> в Gleam делает код читаемым слева направо, а не изнутри наружу:

// Вместо этого:
json.to_string(response.build(handle_input(parse_request(raw_body))))

// Пишешь это:
raw_body
|> parse_request
|> handle_input
|> response.build
|> json.to_string

После pipe-оператора возвращаться к вложенным вызовам физически неприятно.

Иммутабельность по умолчанию

Всё иммутабельно. Нет переменных, которые кто-то может изменить из другого потока. Нет race conditions на уровне данных. Для бэкенда, обрабатывающего конкурентные запросы — это не просто удобство, это архитектурная гарантия.

Сообщения об ошибках компилятора

Gleam унаследовал от Elm-традиции человечные сообщения об ошибках. Когда типы не совпадают, компилятор не просто говорит «type mismatch» — он объясняет, что ожидалось, что получено, и часто подсказывает, как исправить. После gcc и даже rustc — это глоток свежего воздуха.

Что бесит

  • Нет мутабельных переменных совсем. Иногда хочется просто инкрементировать счётчик. Мне сложно игнорировать свои 20 лет программирования с мутабельными переменными. В Gleam для этого нужен рекурсивный вызов или fold. Идеологически чисто, практически — иногда утомляет.

  • Нет раннего return. В Go я пишу guard clause — проверил условие, вернулся. В Gleam каждый путь должен быть частью выражения. Для сложных валидаций это удлиняет код.

  • Маленькое сообщество. Если ты застрял — Stack Overflow не поможет. Discord Gleam — основной канал поддержки, и он отзывчивый, но это не масштаб Go или Python.

  • Отсутствие IDE-поддержки уровня GoLand/IntelliJ. Gleam LSP работает, но рефакторинг, навигация, автодополнение — всё на уровне «достаточно, но не более».

  • Документация библиотек. Hexdocs есть, но многие пакеты документированы по принципу «типы говорят сами за себя». Иногда говорят. Иногда — молчат.

Деплой: скучно и надежно

У меня простой проект. Поэтому: никакого Docker, никакого Kubernetes.

Вот весь деплой:

  • gleam export erlang-shipment — собирает релиз

  • scp на сервер

  • systemd unit для запуска и рестарта

  • Nginx как reverse proxy с SSL

BEAM-релиз — это самодостаточная директория с Erlang runtime и скомпилированным кодом. Работает как обычный процесс. Потребление памяти — около 30–50 МБ. Для side-проекта на VPS за 500 рублей в месяц — более чем достаточно.

Стоило ли оно того?

Да. Но с оговорками.

Стоило, потому что: я перестроил своё мышление. После двадцати лет императивного кода я теперь думаю типами и паттернами, а не циклами и мутациями. Это сделало меня лучшим инженером даже в Go — я стал чаще использовать интерфейсы как discriminated unions, строже обрабатывать ошибки, избегать мутабельного состояния.

Стоило, потому что: Gleam заставляет проектировать перед кодированием. Когда у тебя нет null, нет исключений, нет мута>ций — ты вынужден продумать все пути данных заранее. Это медленнее на старте и быстрее на дистанции.

С оговоркой: для production-системы с SLA и on-call я бы пока выбрал Go или Elixir. Экосистема Gleam не готова для «взрослого» production, где нужны десятки интеграций из коробки. Но для side-проекта, pet-проекта, микросервиса с узким скоупом — Gleam идеален.

Кому стоит попробовать Gleam

  • Go/Java/C#-разработчикам, которые хотят попробовать функциональное программирование без боли Haskell. Gleam — самый мягкий вход в мир типизированного функционального программирования.

  • Elixir-разработчикам, которым не хватает статических типов. Gleam компилируется в Erlang и может использовать любые Erlang/Elixir-библиотеки.

  • Тем, кто пишет конечные автоматы, парсеры, обработчики протоколов — задачи, где важна корректность и полнота обработки всех случаев.

  • Тем, кто устал от if err != nil и хочет попробовать мир, где обработка ошибок — не ритуал, а часть дизайна.

Вместо заключения

Gleam — не серебряная пуля. Это молодой язык с маленькой экосистемой и амбициозными идеями. Но он решает реальную проблему: даёт статическую типизацию и функциональную чистоту без порога входа Haskell и без когнитивной нагрузки Rust.

Если вы двадцать лет писали императивный код и чувствуете, что стагнируете — попробуйте написать что-нибудь на Gleam. Необязательно целый бэкенд. Начните с Exercism, потом — CLI-утилита, потом — маленький API. Ваш мозг скажет спасибо.

А если напишете библиотеку — вы автоматически станете одним из ведущих контрибьюторов экосистемы. Нас пока мало. Заходите.

P.S. Если интересно, в следующей статье покажу подробнее архитектуру конечного автомата на типах Gleam — как моделировать диалоговые сценарии так, чтобы невалидные переходы были невозможны на уровне компиляции.

Комментарии (33)


  1. Surrogate
    20.03.2026 17:06

    Я думал GLEAM - это другое)

    Специфический профсоюз внутри M$FT


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Представляю, что выдаёт гугл тем, кто впервые пытается загуглить этот язык. Зато запоминается!


  1. napolskih
    20.03.2026 17:06

    Спасибо за контент по BEAM, Erlang и Elixir.

    Тоже имею 20+ лет опыта разработки и пару лет назад погрузился в Elixir. Давно меня так ничего не впечатляло. Удалось получить работу, удалось написать несколько фичей на нем в прод, но пришлось вернуться к Ruby и менеджменту. Надеюсь еще написать что-то на Elixir, а позже может быть и на Gleam.


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Спасибо! Очень знакомая история — после 20 лет в IT думаешь, что тебя уже сложно чем-то удивить. А потом пробуешь ФП на BEAM — и вдруг снова чувствуешь себя джуном, который открывает что-то по-настоящему новое. Давно забытое ощущение.

      Интересно, что вы пришли через Elixir. Я сознательно выбрал Gleam именно из-за статических типов — после Go хотелось строгости, но без когнитивной нагрузки Rust. А что в Elixir впечатлило больше всего — BEAM как платформа или сам язык?


      1. napolskih
        20.03.2026 17:06

        Конечно и BEAM как платформа, а так же концепции заложенные в язык и тулинг. Иммутабельность, паттерн-матчинг, пайп-оператов, with, док-тесты, качество документации. Многое, всего уже не помню тк пришлось отойти от темы (сбили на взлете, сокращения).


        1. AlexeiZhuravlev Автор
          20.03.2026 17:06

          Да, пайп-оператор и иммутабельность — это то, что меняет голову навсегда. Я теперь даже в Go-коде ловлю себя на том, что думаю пайпами.

          Сочувствую насчёт сокращений. Но BEAM-мышление не теряется — когда будет возможность вернуться, порог входа будет совсем другой.


  1. StanEgo
    20.03.2026 17:06

    Зачем он C#-разработчикам, у которых есть F#? С exhaustive pattern matching, pipe-operator, мутабельными переменными и computational expression, на которых красиво делаются и state machines, и ранний return. Всё это в итоге можно собрать в один бинарник как в Go.


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      F# — отличный язык, и для C#-разработчиков это действительно более естественный шаг в ФП — остаёшься на .NET, вся огромная и зрелая экосистема под рукой. Спорить с этим было бы странно.

      Разница — в рантайме. F# работает на CLR, Gleam — на BEAM. Это разные модели конкурентности: лёгкие процессы с изоляцией ошибок, supervision trees, hot code reload. Для голосовых навыков, чатов, real-time систем это имеет значение. Для типичного бэкенда — нет, и F# будет практичнее.

      А насчёт мутабельных переменных — я в статье честно написал, что их отсутствие в Gleam иногда бесит. F# тут прагматичнее.


  1. remindscope
    20.03.2026 17:06

    В современных версиях Java есть все те же удобства, куча зрелых библиотек, а пайпы можно писать через optional или stream.

    Может я чего-то недопонял из материала, но для меня как джависта серьезных причин присмотреться к этому языку не нашлось.


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Справедливо. Если Java закрывает ваши задачи и экосистема устраивает — нет смысла менять ради синтаксического сахара.

      Разница глубже, чем пайпы. Optional и Stream — это ФП-элементы поверх императивного языка. В Gleam иммутабельность и exhaustive pattern matching — не опция, а единственный способ писать код. Нет null, нет исключений, нет мутабельного состояния. Компилятор не даёт забыть ветку в case — вообще, ни одну. В Java sealed classes + switch expressions приближаются к этому, но не заставляют.


      1. DOKL57
        20.03.2026 17:06

        Тут мог бы подойти котлин, если хочется уйти от многословности java. Получить null-safety на уровне типов и exhaustive pattern matching через when+sealed (компилятор тоже не даст забыть ветку). Плюс он поощряет иммутабельность и функциональщину. Но несмотря на это, всё же является мультипарадигменным ЯП. Ещё очевидный плюс - большая экосистема вокруг языка. Но не факт, что она тут пригодится.
        Если цель - писать чистый ФП-код и уйти от минусов jvm к минусам beam, то gleam действительно кажется хорошим вариантом, если исходить из статьи (не писал на нём).
        А в elixir, кстати, скоро появится gradual typing, сейчас он уже есть в RC-ветке.


        1. napolskih
          20.03.2026 17:06

          В BEAM большая ценность это сама платформа. Акторная модель, возможность запустить, к примеру, 300к параллельных процессов и сохранять отзывчивость. Построение кластерных приложений. Я не вижу конкурентов. За развитием Elixir слежу.


    1. t1nker
      20.03.2026 17:06

      NullPointerException пугает даже меня - не джависта. Одна из причин присмотреться к другим языкам


  1. 9lLLLepuLLa
    20.03.2026 17:06

    Можете развернуть мысль, в чем, как вы считаете, боль хаскеля ?


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Говорю как человек с двадцатью годами императивного кода, не как эксперт по Haskell. Моя «боль» — это боль порога входа. Чтобы написать HTTP-сервер на Gleam, мне хватило выходных. Чтобы сделать то же на Haskell, мне сначала нужно понять, почему IO — это монада и зачем мне это знать.

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

      Уверен, что для тех, кто уже в Haskell, это не боль, а сила. Но я писал статью для таких же как я — Go/Java/C#-разработчиков, которые хотят попробовать ФП без огромных временных затрат.


      1. 9lLLLepuLLa
        20.03.2026 17:06

        Спасибо. А вот скажите как обстоят дела с логированием тут ? Можно встроить это в pipe ? И как вообще работать с IO ?


        1. AlexeiZhuravlev Автор
          20.03.2026 17:06

          Логирование в Gleam можно условно разделить так:

          Дебаг. Встроенное ключевое слово echo — вставляешь в любое место пайпа, оно пропускает значение через себя и печатает в stderr с указанием файла и строки:

          [1, 2, 3]
          |> list.map(fn(x) { x + 1 })
          |> echo
          |> list.map(fn(x) { x * 2 })

          Билд-тул предупредит, если забудете убрать перед публикацией. Есть ещё io.debug — делает то же самое, но без метаданных.

          Боевое логирование. Пакет logging — обёртка над Erlang logger. Уровни, форматирование, всё стандартно. Есть glimt — более продвинутый, со структурированным JSON-выводом и контекстами.


      1. Filipp42
        20.03.2026 17:06

        Мне вот интересен Lean 4. Это одновременно и язык программирования, и помощник для доказательств. Он помогает изучать математику. Если ты доказал теорему, и верифицировал доказательство в Lean, можно быть практически уверенным, что доказательство правильное.


        1. AlexeiZhuravlev Автор
          20.03.2026 17:06

          Интересная ниша — язык как инструмент доказательства, а не только разработки. С Lean не работал. Ждем от вас статью про него))


      1. nice_nick_matter
        20.03.2026 17:06

        IO является монадой потому, что ввод/вывод в haskell сделали на монадах. Почему нужно знать, что IO монада? Потому, что нужно уметь работать со значениями "упакованными" в монаду IO, а это умеет делать оператор bind -- >>= (еще это умеют делать unsafePerformIO и друзья, но unsafe в названии у них не просто так! И со значениями в монаде IO надо работать именно с помощью bind). Все просто!


        1. AlexeiZhuravlev Автор
          20.03.2026 17:06

          Спасибо за объяснение! Вы как раз подтверждаете мою мысль — для вас это «всё просто», и я верю. Но в вашем объяснении уже есть «монада», «bind», «>>=», «unsafePerformIO» и «упаковка значений». Для обычного разработчика, который хочет напечатать строку в консоль, это пять новых концепций до первого результата. В Gleam это io.println("hello") — и готово.


  1. evgeniy_kudinov
    20.03.2026 17:06

    Спасибо за краткое знакомство с Gleam. Буду рад дополнительным материалам по нему.


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Спасибо! Постараюсь подготовить интересный материал.


  1. ImagineTables
    20.03.2026 17:06

    Оператор |> в Gleam делает код читаемым слева направо, а не изнутри наружу

    Крутецки выглядит. А можно передать собранную пайпу как лямбду аргументом куда-нибудь?


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Сам пайп |> — это синтаксический сахар, а не значение, его нельзя передать куда-то как аргумент. Но можно собрать цепочку в анонимную функцию и передать её:

      let transform = fn(x) {
        x
        |> string.trim
        |> string.uppercase
        |> string.append("!")
      }
      
      list.map(items, transform)


      1. ImagineTables
        20.03.2026 17:06

        Я перестал понимать. Это во что развернётся?

        Если…

        raw_body
        |> parse_request
        |> handle_input
        |> response.build
        |> json.to_string
        

        …разворачивается в json.to_string(response.build(handle_input(parse_request(raw_body)))), то…

        x
          |> string.trim
          |> string.uppercase
          |> string.append("!")
        

        …должно развернуться в string.append(string.uppercase(string.trim(x)), "!")?

        Если да, то как-то упячно выглядит. По сравнению с традиционным:

        x
          .trim()
          .uppercase()
          .append("!")
        

        Я изначально подумал, что |> удобен, чтобы собирать конвейер из разных частей. Например, handle_input, response.build и json.to_string. А для набора операций обычной иммутабельной строки зачем? Где неявно передаётся this?

        А вообще, меня эта тема зацепила тем, что я давно думаю, как сделать идеальные чейнинги (на JS или других, более функциональных, языках), где одним выражением можно результат первого filter разбить на два других filter, и с каждым сделать что-то своё, без промежуточных переменных. Когда говорят, что «jQuery устарел», в этом отношении это правда — он так не умеет )) А хотелось бы.


        1. AlexeiZhuravlev Автор
          20.03.2026 17:06

          Да, разворачивается именно так. И вы правы — для строки x.trim().uppercase().append("!") читается лучше. Но в этом и суть: method chaining работает, пока все методы принадлежат одному объекту. Как только нужно протащить данные через функции из разных модулей — parse_request, потом handle_input, потом response.build — методы кончаются, а пайп продолжает работать.

          В Gleam нет методов и нет this — функции принадлежат модулям, а не данным. Поэтому string.trim(x) вместо x.trim(). Пайп компенсирует отсутствие методов — и делает это универсально, потому что работает с любой функцией, а не только с методами одного типа.

          Кстати, ваша идея про разветвление после filter — это по сути то, что в ФП делается через let. Не промежуточные переменные в императивном смысле, а именованные привязки:

          let filtered = items |> list.filter(is_active)
          let group_a = filtered |> list.filter(is_premium)
          let group_b = filtered |> list.filter(fn(x) { !is_premium(x) })

          Пайп не пытается заменить всё — он для линейных цепочек. Когда поток разветвляется — let и отдельные пайпы.


          1. ImagineTables
            20.03.2026 17:06

            Ясно, спасибо.

            Кстати, ваша идея про разветвление после filter — это по сути то, что в ФП делается через let. Не промежуточные переменные в императивном смысле, а именованные привязки

            Это меня как раз и не устраивает. У вас в примере group_a = filtered |> list.filter(is_premium). Я полагаю, это только для примера, в жизни, понятное дело, напишут не group_a, а premium_group, и нарушится DRY: is_premium УЖЕ описывает ситуацию. Если всерьёз рассмотреть использование имён типа a, это не способ избежать нарушений DRY, это способ замаскировать нарушение. Единственный способ избежать нарушений DRY — вообще избегать всего поименованнного, поэтому я и думаю, как такие вещи писать одним выражением. Хотя бы запись чтоб была исключающая противоречия, уж потом можно подумать про атомарность транзакции и т.п.


            1. AlexeiZhuravlev Автор
              20.03.2026 17:06

              Да для примера. Но мне кажется, тут не нарушение DRY, а два разных уровня абстракции. is_premium — это предикат. premium_group — это результат применения предиката к конкретному набору. Они не дублируют друг друга — они на разных этажах.


  1. blackyblack
    20.03.2026 17:06

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

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


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Про Rust — согласен, что он мощнее как язык. Но для моей задачи (голосовой навык, FSM, Redis, конкурентные сессии) его сила избыточна, а BEAM под капотом Gleam даёт модель конкурентности, которой в Rust нет из коробки — лёгкие процессы, изоляция ошибок, supervision trees. Это не про синтаксис, а про рантайм.

      Про Go — я в основном занимаюсь архитектурой, кода пишу мало. Go в нашем стеке — не мой личный выбор, а стандарт, который выбирает компания. И его выбирают именно за то, за что вы его критикуете — он простой, предсказуемый, любой новый разработчик входит в проект за короткий срок. Для команд в 10-50 человек это перевешивает элегантность. Отвращение к Go я понимаю, сам в статье написал про бесконечные «if err != nil» — но архитектурные решения принимаются не по ощущениям одного человека, а по стоимости поддержки для всей команды.


  1. fenom82
    20.03.2026 17:06

    Как будто бы Scala хорошо подошла. Не рассматривали её в качестве альтернативы?

    Более зрелые тулы, функциональный подход, грин треды, авторы где если хотите


    1. AlexeiZhuravlev Автор
      20.03.2026 17:06

      Scala не рассматривал, честно. Она мощная, но для side-проекта мне был важен минимальный порог входа. Gleam можно выучить за выходные — у Scala порог существенно выше, особенно если до этого не был в JVM-мире.