Спасибо за объяснение! Вы как раз подтверждаете мою мысль — для вас это «всё просто», и я верю. Но в вашем объяснении уже есть «монада», «bind», «>>=», «unsafePerformIO» и «упаковка значений». Для обычного разработчика, который хочет напечатать строку в консоль, это пять новых концепций до первого результата. В Gleam это io.println("hello") — и готово.
Scala не рассматривал, честно. Она мощная, но для side-проекта мне был важен минимальный порог входа. Gleam можно выучить за выходные — у Scala порог существенно выше, особенно если до этого не был в JVM-мире.
Да для примера. Но мне кажется, тут не нарушение DRY, а два разных уровня абстракции. is_premium — это предикат. premium_group — это результат применения предиката к конкретному набору. Они не дублируют друг друга — они на разных этажах.
Да, разворачивается именно так. И вы правы — для строки 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 и отдельные пайпы.
Про Rust — согласен, что он мощнее как язык. Но для моей задачи (голосовой навык, FSM, Redis, конкурентные сессии) его сила избыточна, а BEAM под капотом Gleam даёт модель конкурентности, которой в Rust нет из коробки — лёгкие процессы, изоляция ошибок, supervision trees. Это не про синтаксис, а про рантайм.
Про Go — я в основном занимаюсь архитектурой, кода пишу мало. Go в нашем стеке — не мой личный выбор, а стандарт, который выбирает компания. И его выбирают именно за то, за что вы его критикуете — он простой, предсказуемый, любой новый разработчик входит в проект за короткий срок. Для команд в 10-50 человек это перевешивает элегантность. Отвращение к Go я понимаю, сам в статье написал про бесконечные «if err != nil» — но архитектурные решения принимаются не по ощущениям одного человека, а по стоимости поддержки для всей команды.
Дебаг. Встроенное ключевое слово 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-выводом и контекстами.
Сам пайп |> — это синтаксический сахар, а не значение, его нельзя передать куда-то как аргумент. Но можно собрать цепочку в анонимную функцию и передать её:
let transform = fn(x) {
x
|> string.trim
|> string.uppercase
|> string.append("!")
}
list.map(items, transform)
Говорю как человек с двадцатью годами императивного кода, не как эксперт по Haskell. Моя «боль» — это боль порога входа. Чтобы написать HTTP-сервер на Gleam, мне хватило выходных. Чтобы сделать то же на Haskell, мне сначала нужно понять, почему IO — это монада и зачем мне это знать.
Порог входа — не в синтаксисе, а в количестве концепций, которые нужно усвоить до того, как начнёшь писать что-то полезное. Монады, функторы, классы типов, ленивые вычисления по умолчанию, IO как монада. Каждая по отдельности понятна, но все вместе — это серьёзная нагрузка на мозг.
Уверен, что для тех, кто уже в Haskell, это не боль, а сила. Но я писал статью для таких же как я — Go/Java/C#-разработчиков, которые хотят попробовать ФП без огромных временных затрат.
F# — отличный язык, и для C#-разработчиков это действительно более естественный шаг в ФП — остаёшься на .NET, вся огромная и зрелая экосистема под рукой. Спорить с этим было бы странно.
Разница — в рантайме. F# работает на CLR, Gleam — на BEAM. Это разные модели конкурентности: лёгкие процессы с изоляцией ошибок, supervision trees, hot code reload. Для голосовых навыков, чатов, real-time систем это имеет значение. Для типичного бэкенда — нет, и F# будет практичнее.
А насчёт мутабельных переменных — я в статье честно написал, что их отсутствие в Gleam иногда бесит. F# тут прагматичнее.
Справедливо. Если Java закрывает ваши задачи и экосистема устраивает — нет смысла менять ради синтаксического сахара.
Разница глубже, чем пайпы. Optional и Stream — это ФП-элементы поверх императивного языка. В Gleam иммутабельность и exhaustive pattern matching — не опция, а единственный способ писать код. Нет null, нет исключений, нет мутабельного состояния. Компилятор не даёт забыть ветку в case — вообще, ни одну. В Java sealed classes + switch expressions приближаются к этому, но не заставляют.
Спасибо! Очень знакомая история — после 20 лет в IT думаешь, что тебя уже сложно чем-то удивить. А потом пробуешь ФП на BEAM — и вдруг снова чувствуешь себя джуном, который открывает что-то по-настоящему новое. Давно забытое ощущение.
Интересно, что вы пришли через Elixir. Я сознательно выбрал Gleam именно из-за статических типов — после Go хотелось строгости, но без когнитивной нагрузки Rust. А что в Elixir впечатлило больше всего — BEAM как платформа или сам язык?
"Видимо iq ниже 70 — это обязательное требование при приёме на работу на государственное телевидение." - это оценочное суждение автора статьи. Какими фактами оно обосновано? На основании чего вы можете оскорблять журналистов? Вы сами никогда не косячите? А если ошибаетесь, то у вас IQ падает до 70 пунктов?
Даже если будет выбор между "гарантированно дать в зад машине впереди" и "начать рулить и будь что будет", я всегда выберу первое. Так же требуют и ПДД.
Спасибо за объяснение! Вы как раз подтверждаете мою мысль — для вас это «всё просто», и я верю. Но в вашем объяснении уже есть «монада», «bind», «>>=», «unsafePerformIO» и «упаковка значений». Для обычного разработчика, который хочет напечатать строку в консоль, это пять новых концепций до первого результата. В Gleam это
io.println("hello")— и готово.Scala не рассматривал, честно. Она мощная, но для side-проекта мне был важен минимальный порог входа. Gleam можно выучить за выходные — у Scala порог существенно выше, особенно если до этого не был в JVM-мире.
Да для примера. Но мне кажется, тут не нарушение DRY, а два разных уровня абстракции.
is_premium— это предикат.premium_group— это результат применения предиката к конкретному набору. Они не дублируют друг друга — они на разных этажах.Да, разворачивается именно так. И вы правы — для строки
x.trim().uppercase().append("!")читается лучше. Но в этом и суть: method chaining работает, пока все методы принадлежат одному объекту. Как только нужно протащить данные через функции из разных модулей —parse_request, потомhandle_input, потомresponse.build— методы кончаются, а пайп продолжает работать.В Gleam нет методов и нет
this— функции принадлежат модулям, а не данным. Поэтомуstring.trim(x)вместоx.trim(). Пайп компенсирует отсутствие методов — и делает это универсально, потому что работает с любой функцией, а не только с методами одного типа.Кстати, ваша идея про разветвление после filter — это по сути то, что в ФП делается через
let. Не промежуточные переменные в императивном смысле, а именованные привязки:Пайп не пытается заменить всё — он для линейных цепочек. Когда поток разветвляется — let и отдельные пайпы.
Интересная ниша — язык как инструмент доказательства, а не только разработки. С Lean не работал. Ждем от вас статью про него))
Про Rust — согласен, что он мощнее как язык. Но для моей задачи (голосовой навык, FSM, Redis, конкурентные сессии) его сила избыточна, а BEAM под капотом Gleam даёт модель конкурентности, которой в Rust нет из коробки — лёгкие процессы, изоляция ошибок, supervision trees. Это не про синтаксис, а про рантайм.
Про Go — я в основном занимаюсь архитектурой, кода пишу мало. Go в нашем стеке — не мой личный выбор, а стандарт, который выбирает компания. И его выбирают именно за то, за что вы его критикуете — он простой, предсказуемый, любой новый разработчик входит в проект за короткий срок. Для команд в 10-50 человек это перевешивает элегантность. Отвращение к Go я понимаю, сам в статье написал про бесконечные «if err != nil» — но архитектурные решения принимаются не по ощущениям одного человека, а по стоимости поддержки для всей команды.
Спасибо! Постараюсь подготовить интересный материал.
Логирование в Gleam можно условно разделить так:
Дебаг. Встроенное ключевое слово
echo— вставляешь в любое место пайпа, оно пропускает значение через себя и печатает в stderr с указанием файла и строки:Билд-тул предупредит, если забудете убрать перед публикацией. Есть ещё
io.debug— делает то же самое, но без метаданных.Боевое логирование. Пакет
logging— обёртка над Erlang logger. Уровни, форматирование, всё стандартно. Естьglimt— более продвинутый, со структурированным JSON-выводом и контекстами.Сам пайп
|>— это синтаксический сахар, а не значение, его нельзя передать куда-то как аргумент. Но можно собрать цепочку в анонимную функцию и передать её:Говорю как человек с двадцатью годами императивного кода, не как эксперт по Haskell. Моя «боль» — это боль порога входа. Чтобы написать HTTP-сервер на Gleam, мне хватило выходных. Чтобы сделать то же на Haskell, мне сначала нужно понять, почему IO — это монада и зачем мне это знать.
Порог входа — не в синтаксисе, а в количестве концепций, которые нужно усвоить до того, как начнёшь писать что-то полезное. Монады, функторы, классы типов, ленивые вычисления по умолчанию, IO как монада. Каждая по отдельности понятна, но все вместе — это серьёзная нагрузка на мозг.
Уверен, что для тех, кто уже в Haskell, это не боль, а сила. Но я писал статью для таких же как я — Go/Java/C#-разработчиков, которые хотят попробовать ФП без огромных временных затрат.
F# — отличный язык, и для C#-разработчиков это действительно более естественный шаг в ФП — остаёшься на .NET, вся огромная и зрелая экосистема под рукой. Спорить с этим было бы странно.
Разница — в рантайме. F# работает на CLR, Gleam — на BEAM. Это разные модели конкурентности: лёгкие процессы с изоляцией ошибок, supervision trees, hot code reload. Для голосовых навыков, чатов, real-time систем это имеет значение. Для типичного бэкенда — нет, и F# будет практичнее.
А насчёт мутабельных переменных — я в статье честно написал, что их отсутствие в Gleam иногда бесит. F# тут прагматичнее.
Справедливо. Если Java закрывает ваши задачи и экосистема устраивает — нет смысла менять ради синтаксического сахара.
Разница глубже, чем пайпы. Optional и Stream — это ФП-элементы поверх императивного языка. В Gleam иммутабельность и exhaustive pattern matching — не опция, а единственный способ писать код. Нет null, нет исключений, нет мутабельного состояния. Компилятор не даёт забыть ветку в case — вообще, ни одну. В Java sealed classes + switch expressions приближаются к этому, но не заставляют.
Да, пайп-оператор и иммутабельность — это то, что меняет голову навсегда. Я теперь даже в Go-коде ловлю себя на том, что думаю пайпами.
Сочувствую насчёт сокращений. Но BEAM-мышление не теряется — когда будет возможность вернуться, порог входа будет совсем другой.
Представляю, что выдаёт гугл тем, кто впервые пытается загуглить этот язык. Зато запоминается!
Спасибо! Очень знакомая история — после 20 лет в IT думаешь, что тебя уже сложно чем-то удивить. А потом пробуешь ФП на BEAM — и вдруг снова чувствуешь себя джуном, который открывает что-то по-настоящему новое. Давно забытое ощущение.
Интересно, что вы пришли через Elixir. Я сознательно выбрал Gleam именно из-за статических типов — после Go хотелось строгости, но без когнитивной нагрузки Rust. А что в Elixir впечатлило больше всего — BEAM как платформа или сам язык?
Факты это прекрасно и с ними я не спорю. Но откуда взялось значение именно 70?
"Видимо iq ниже 70 — это обязательное требование при приёме на работу на государственное телевидение." - это оценочное суждение автора статьи. Какими фактами оно обосновано? На основании чего вы можете оскорблять журналистов? Вы сами никогда не косячите? А если ошибаетесь, то у вас IQ падает до 70 пунктов?
Алиса, очень интересная и глубокая статья. Спасибо большое за публикацию! Интересно и познавательно было прочитать!
Это не запрещает одновременно тормозить и рулить.
Где конкретно ПДД это требуют?