An Algebraic Language for the Manipulation of Symbolic Expressions (1958) [pdf]
Эта работа представляет спецификацию алгебраического языка для манипуляции символьными выражениями, разработанного Джоном Маккарти. Язык предназначен для программирования формальных математических процессов (алгебраическое упрощение, дифференцирование, интегрирование), написания компиляторов (кроме ввода/вывода) и эвристических программ, где удобно представлять деревья альтернативных действий. Он особенно эффективен для работы с выражениями переменной длины и структуры, имеющими подобподвыражения, но менее удобен для списков фиксированной длины.
Выражения в языке представляются списками, где каждый элемент занимает машинное слово, содержащее данные и адрес следующего элемента. Маккарти отмечает, что удобство алгебраической нотации связано с возможностью композиции функций без именования промежуточных результатов и построения сложных структур путем вложения вызовов функций, формирующих списки. Ключевыми особенностями языка являются поддержка рекурсии с автоматическим сохранением промежуточных результатов в структурах списков и мощные условные выражения, решающие проблему выбора операций на основе результатов тестов. Маккарти ссылается на ранние работы Ньюэлла, Саймона и Шоу по эвристическому программированию и Гелентера в геометрической программе как предшественников.
Комментарии (11)
- Появление первых отчетов о LISP в 1958 году стало поворотным моментом, поскольку они ввели ключевые концепции, которые позже стали основой для многих языков программирования.
- Исторически важно, что McCarthy и его команда в MIT в 1958 году разработали и документировали эти ранние отчеты, включая в них идеи, которые до сих пор используются в современных языках.
- Отчеты показали, как LISP влиял на развитие компьютерных наук, включая идеи о списковой обработке, рекурсии, и функциональном программировании.
- Несмотря на то, что эти отчеты были написаны более 60 лет назад, они до сих пор влияют на разработку языков программирования и остаются актуальными для изучения истории ИИ и вычислительных наук.
- Некоторые участники обсуждения подчеркнули, что эти отчеты не только исторически важны, но и могут быть использованы для образовательных целей, чтобы изучать основы компьютерных наук и эволюцию языков программирования.
Why I love OCaml (2023) 🔥 Горячее 💬 Длинная дискуссия
Автор делится своим опытом работы с различными языками программирования, объясняя, почему OCaml стал его любимым языком. Он начал с Haskell, который оценил за функциональное программирование и статическую типизацию, но столкнулся с его сложностью и медленной компиляцией. Позже он попробовал Go, который понравился своей простотой, скоростью компиляции и хорошими инструментами, но разочаровал многословностью обработки ошибок и отсутствием функциональных возможностей. OCaml, по мнению автора, сочетает в себе лучшее из обоих миров: функциональные конструкции, статические гарантии, быструю компиляцию, простую семантику выполнения и отличную документацию. Особо отмечается, что OCaml компилируется в один статический бинарный файл, как Go, но при этом имеет более мощную систему типов и REPL. Автор считает, что создатели OCaml обладают хорошим вкусом, а язык представляет собой идеальный баланс между простотой и выразительностью.
Комментарии (267)
- OCaml не стал мейнстримом из-за синтаксиса, отсутствия нативных библиотек и слабой экосистемы, но его идеи (pattern-matching, type inference, algebraic data types) уже давно живут в Rust, Swift, TypeScript и даже Go.
- Фактически OCaml — это «нативный» вариант F#, но без .NET-экосистемы, а F# — это OCaml без его синтаксиса и с .NET вместо OCaml-стандартной библиотеки.
- Попытка ReasonML привнести более C-подобный синтаксис вместо ужасающего синтаксиса OCaml закончилась тем, что Facebook забросил проект, а вся индустрия JS-инструментов осталась без единого стандарта.
- Попытка Facebook-а внедрить Reason вместо TypeScript внутри Facebook показала, что даже если синтаксис и не является проблемой, то отсутствие единого стандарта для сборки и пакетов в JS-мире оставляет язык без шанса.
- Несмотря на то, что OCaml — это язык с 25-летней историей, он не имеет ни мультиплатформенной сборки, ни нормального менеджера пакетов, ни нормального REPL, что делает его неподготовленным к работе вне Unix-подобных систем.
Transducer: Composition, abstraction, performance (2018)
В функциональном программировании высшие функции вроде map, filter и fold можно определить через reduce, показывая что "всё сворачивается". Эти функции зависят от коллекций лишь через операцию conj, что открывает путь к абстракции. В Clojure это реализуется через трансдьюсеры — функции, принимающие шаг обработки и возвращающие его модифицированную версию.
Трансдьюсеры позволяют создавать переиспользуемые преобразования данных, работающие с различными структурами, от списков до потоков. Их ключевое преимущество — композиция: несколько трансдьюсеров могут объединяться без потери производительности. Реализация поддерживает разные арности (0, 1 или 2 аргумента), что делает их универсальными для различных сценариев обработки данных. Такой подход обеспечивает как переиспользование кода, так и высокую производительность за счет meaningful абстракции.
Комментарии (5)
- Обсуждение началось с предложения рассмотреть альтернативное, более мягкое введение в трансдюсеры как функции
a -> List<b>, подчеркивая их полиморфизм и простоту. - Участники поделились опытом, что новички в Clojure часто переоценивают
reduceи пытаются заменить им циклы, что приводит к запутанному коду, в то время как трансдюсеры предлагают более чистый и декларативный подход. - Сообщество единодушно подтвердило, что трансдюсеры действительно делают код более выразительным и устойчивым к ошибкам, особенно когда дело касается обработки коллекций.
- В итоге обсуждение сошлось на том, что хотя трансдюсеры и требуют некоторого переосмысления привычных паттернов, их преимущества в выразительности и надежности делают их ценным инструментом для Clojure разработчика.
Control structures in programming languages: from goto to algebraic effects
Книга Ксавье Леруа исследует эволюцию управляющих конструкций в языках программирования, от ранних операторов goto до современных алгебраических эффектов. Работа представляет собой историческое путешествие по дизайну языков, уделяя особое внимание механизмам контроля выполнения программ. Книга охватывает структурированное программирование 1960-х, генераторы и корутины в императивных языках, а также продолжения и операторы контроля в функциональных языках.
Книга разделена на четыре части: управляющие конструкции для императивных языков, операторы контроля для функциональных языков, от исключений к алгебраическим эффектам и обработчикам, а также рассуждения о контроле и эффектах. Особую ценность представляют многочисленные примеры кода на разных языках — от Fortran и Algol 60 до OCaml и Haskell. Работа сочетает историю, практические примеры и теорию, предлагая оригинальный сравнительный взгляд на языки программирования и обширное введение в алгебраические эффекты — современную область исследований в PL.
Комментарии (29)
- Критика исключений как "glorified come from", сравнение с POSIX сигналами и setjmp/longjmp.
- Дискуссия о checked исключениях в Java vs подход "ошибки как данные" для ожидаемых сбоев.
- Вопрос производительности алгебраических эффектов и ответ: реализация возможна через монады, оптимизирована в OCaml 5.
- Упоминание Xavier Leroy как автора CompCert, LinuxThreads и ключевой фигуры OCaml/Rocq.
- Шутка про INTERCAL и "come from" как пример необычного синтаксиса.
How I fell in love with Erlang 🔥 Горячее 💬 Длинная дискуссия
Автор рассказывает о своем пути к любви к Erlang, начавшегося с непонимания базовых концепций программирования в восемь лет, когда столкнулся с выражением "X = X + 1" в BASIC, показавшимся ему математической ложью. В университете он столкнулся с такими же трудностями при изучении C, но продолжил учиться через практику и эксперименты. Переломный момент наступил, когда партнер по бриджу спросил, как суммировать числа от 1 до 10 без циклов, что привело его к открытию рекурсии в Prolog — математически чистого подхода, где описывалось "что" является, а не "как" вычислять.
Знакомство с Erlang произошло случайно на турнире по бриджу от шведского игрока, который описал его как функциональный язык для распределенных, отказоустойчивых систем. Автор был поражен возможностью простого обмена сообщениями между разными узлами без сложных протоколов, демонстрируя пример ping-pong на Erlang. Этот язык, сочетающий функциональность, распределенность и отказоустойчивость, стал тем, что он искал с детства — способом программирования, где математика говорит правду.
Комментарии (209)
- @az09mugen отмечает полезность функции
> cd ..в конце статьи за простоту и мобильную доступность. - @az09mugen предполагает наличие пасхалки (easter egg) в этой функции.
- @Gleamball выражает благодарность за выступление и обмен информацией.
Simplify your code: Functional core, imperative shell 🔥 Горячее 💬 Длинная дискуссия
Google предлагает разделять код на функциональное ядро и императивную оболочку для упрощения разработки. Функциональное ядро содержит чистую бизнес-логику без побочных эффектов, а императивная оболочка обрабатывает взаимодействие с внешними системами. Такой подход позволяет тестировать логику изолированно и делает код более поддерживаемым. В статье приведен пример кода для отправки уведомлений об окончании подписки, демонстрирующий разницу между смешиванием логики и побочных эффектов и их разделением.
При таком разделении добавление новых функций становится проще - достаточно создать новые чистые функции и переиспользовать существующие. Например, для напоминаний о подписке можно создать функцию generateReminderEmails, используя уже существующую getExpiredUsers. Этот паттерн, впервые описанный Гэри Бернхардтом, помогает создавать более тестируемый, поддерживаемый и адаптивный код, избегая "спагетти" из смешанной логики и побочных эффектов.
Комментарии (170)
- Обсуждение вращается вокруг идеи "functional core, imperative shell" (FCIS) и противоположного ей подхода "generic core, specific shell", а также влияния этих подходов на тестируемость, производительность и читаемость кода.
- Участники обсуждают, что FCIS делает код более тестируемым, но может привести к проблемам с производительностью при работе с большими объемами данных, особенно если язык не поддерживает ленивые коллекции.
- Также обсуждается, что важно разделять логику и эффекты, но пример кода в статье вызывает вопросы, потому что он не демонстрирует лучшие практики, такие как пагинация или фильтрация на уровне базы данных.
- Некоторые участники подчеркивают, что важно не только следовать паттерну, но и использовать здравый смысл, чтобы не плодить сущности, которые не масштабируются, и не создавать ситуаций, где пример кода в статье может быть использован как оправдание для плохого кода.
Programming with Less Than Nothing 🔥 Горячее
Кандидат на собеседовании решает задачу FizzBuzz с помощью комбинаторной логики и лямбда-исчисления в JavaScript, используя лишь базовые комбинаторы S, K и I. Интервьюер всё больше недоумевает, наблюдая, как соискатель последовательно реализует сложнейшие математические операции и структуры данных через композицию этих комбинаторов, включая числа Чёрча, арифметические операции и даже Y-комбинатор для рекурсии.
Кандидат оправдывает свой подход тем, что "JavaScript слишком раздут", и переключается на "ленивый" вариант JavaScript под названием Skoobert, чтобы избежать переполнения стека. В итоге он реализует всё необходимое для FizzBuzz — от сложения и вычитания до списков и операций над ними — исключительно через комбинаторы, демонстрируя глубокое, но совершенно не практичное знание теоретических основ программирования.
Комментарии (139)
- Обсуждение началось с критики формата технического собеседования, где кандидат вместо решения задачи пишет «рассказ о лямбда-исчислении» и тем самым «проходит» тест.
- Дискуссия быстро перешла к тому, что автор статьи, возможно, сам не до конца понял, что именно он хотел показать читателям, и что именно они должны вынести из этого урока.
- Участники треда начали спорить о том, насколько полезно знание комбинаторной логики в повседневной работе программиста, и стоит ли вообще изучать её.
- Некоторые комментаторы отметили, что статья не дала никакого практического обоснования, почему стоит изучать эту тему.
- В итоге, обсуждение сошлось на то, что если кто-то и найдёт в этом какой-то практический смысл, то это будет уже его заслуга, а не автора статьи.
Code from MIT's 1986 SICP video lectures
Репозиторий felipap/sicp-code содержит оцифрованный код из знаменитых лекций MIT SICP 1986 года. Эти лекции, основанные на классическом учебнике "Структура и интерпретация компьютерных программ", считаются одними из самых влиятельных в истории компьютерных наук. Проект представляет собой историческую ценность, сохраняя для потомков примеры кода, которые формировали мышление поколений программистов.
Код в репозитории организован по лекциям и включает примеры на языке Scheme, демонстрирующие фундаментальные концепции функционального программирования и абстракций. Репозиторий служит не только образовательным ресурсом, но и памятником эволюции программирования, показывая, как фундаментальные идеи остаются актуальными десятилетиями после создания.
Комментарии (18)
- SICP и его лекции остаются актуальными, но код из книги 1985 года может не работать в современных реалиях.
- Сообщество активно делится ссылками на ресурсы: форматированная HTML-версия книги, PDF, EPUB, а также исходники примеров кода.
- Обсуждение затрагивает вопрос о том, насколько CS — это наука, и какие именно изменения в парадигме ML влияют на эту точку зрения.
- Участники делятся личными впечатлениями от чтения книги и просмотра лекций, а также обсуждают, как технический долгоживущий код может быть устаревшим.
Free applicatives, the handle pattern, and remote systems
Команда Bellroy автоматизировала работу с ERP-системой, где каждый запрос к API требует предварительных шагов — например, чтобы создать запись, нужен ID другой сущности, что требует дополнительных запросов.
Умный подход: вместо цепочки запросов "запроси A, затем с результатом запроси B" они используют аппликативные функторы. Эти структуры позволяют собрать все нужные запросы в один пакет (отсюда "free applicative"), отправить их разом и потом разобрать ответы, сопоставляя каждый с исходным запросом.
Так они избегают проблем "callback hell" и сохраняют код декларативным. Например, вместо ручной обработки каждого ответа они описывают граф запросов через Free Applicative, а исполнение — дело интерпретатора, который может батчить запросы и парсить ответы.
Особенно изящно это работает с "handle pattern", где каждая операция (типа "получить цену товара") представлена как handle, а реализации могут быть разными (реальная БД vs. мок для тестов). Аппликативы же позволяют комбинировать эти handles в сложные запросы, оставаясь агностичными к источнику данных.
В итоге, Bellroy получает код, где бизнес-логика описана как композиция handles, выполнение оптимизировано (батчинг, кеширование), а тесты пишутся просто — подменой handles на заглушки.
Комментарии (24)
- Bellroy, известный своими кошельками и аксессуарами, использует Haskell и Nix, что вызывает удивление и вопросы о масштабе и прибыльности компании.
- Пользователи обсуждают, как компания, которая начиналась как производитель кошельков, может позволить себе такой стек технологий.
- Обсуждение также затрагивает вопрос о том, как язык программирования и инструменты разработки могут влиять на продукт и его качество.
- Участники также обсуждают, как компания, которая начиналась как производитель кошельков, может позволить себе такой стек технологий.
- В конце, участники обсуждения приходят к выводу, что, возможно, это просто "разработчики, которые ушли в бизнес кошельков", и это может быть просто пример того, как красивые вещи могут появиться из самых неожиданных мест.
How functional programming shaped and twisted front end development
Функциональное программирование принесло во фронтенд мощные абстракции: React с компонентами как функциями от состояния, Redux с предсказуемыми редюсерами и TypeScript для типобезопасности. Однако эти принципы — чистота функций, неизменяемость данных и явные побочные эффекты — вступили в конфликт с природой веба, где DOM мутабелен, CSS каскадируется глобально, а пользовательские события асинхронны и хаотичны. Вместо адаптации к платформе разработчики стали строить слои абстракций, фактически объявляя войну встроенным механизмам браузера.
Это привело к парадоксу: инструменты, созданные для упрощения, добавили сложности, переизобретая роутинг, формы и управление состоянием поверх нативных возможностей. Ценой предсказуемости стала потеря гибкости и производительности, заложенной в вебе десятилетиями. Ключевой вопрос — не отвергать FP-принципы, а искать баланс между их строгостью и адаптивностью платформы, которая по своей сути допускает хаос и мутации.
Комментарии (64)
- Критика излишнего усложнения фронтенд-разработки из-за увлечения функциональным программированием и абстракциями в ущерб простоте и нативным возможностям платформы.
- Споры о читаемости и уместности использования методов массивов (map, filter, reduce) вместо классических циклов, а также о качестве кода на CSS.
- Обсуждение проблем управления состоянием и иерархией данных в UI, включая сравнение React с другими подходами и фреймворками.
- Вопросы к эволюции веб-платформы и стандартов, которые не успевают за потребностями разработчиков и популярными решениями из фреймворков.
- Замечания о качестве статьи-первоисточника: построение на спорных утверждениях (straw man) и продвижение авторской технологии (HTMLX).
Delimited Continuations in Lone Lisp
Lone Lisp теперь поддерживает ограниченные продолжения — мощный механизм управления потоком выполнения. Это позволяет сохранять и восстанавливать состояние вычислений в определённых точках, что открывает путь к реализации генераторов, обработки исключений и других сложных конструкций. Пример кода демонстрирует, как control и transfer работают вместе: (control ...) захватывает контекст, а (transfer ...) передаёт управление, позволяя гибко манипулировать выполнением.
Изначально проблема возникла при реализации итераций: из-за рекурсивной природы интерпретатора и управления стеком на уровне C было невозможно контролировать поток. Решение потребовало переосмысления архитектуры — вместо байткода или трансформаций в стиле продолжений автор выбрал подход, сохраняющий списковую структуру Lisp. Это сложный, но фундаментальный шаг, вдохновлённый классическими работами вроде SICP.
Комментарии (8)
- Wat реализует стек вызовов в пользовательском пространстве для поддержки ограниченных продолжений, что позволяет создавать исключения, ветвление и файберы.
- Вопрос о практическом применении ограниченных продолжений за пределами хобби-проектов и их реальном использовании в промышленности.
- Примеры использования в Ocaml 5 (библиотека Eio), Haskell (GHC, работа приостановлена) и Racket (стандартная модель для обработки ошибок и прерываний).
- Java использует ограниченные продолжения как основу для своих новых "зеленых" потоков (виртуальных потоков).
- Обсуждение потенциальных сфер применения, таких как встраиваемые системы.
Category Theory Illustrated – Natural Transformations
Естественные преобразования — это морфизмы между функторами, позволяющие сравнивать категории не через изоморфизмы объектов, а через согласованные преобразования. Они отражают ключевую идею категорийной теории: важны не сами объекты, а связи между ними.
Изоморфизмы объектов инвариантны, но изоморфизмы категорий слишком жёстки. Естественные преобразования дают более гибкий критерий эквивалентности категорий, где функторы работают согласованно с морфизмами. Это позволяет утверждать, что две категории "одинаковы" в смысле структуры, даже если объекты различаются.
Практически это означает, что свойства, определённые через естественные преобразования, устойчивы к замене категорий на эквивалентные, что фундаментально для переноса конструкций между разными контекстами.
Комментарии (75)
- Обсуждение касается точности философских отсылок (Парменид, Гераклит) и их связи с математическими концепциями.
- Участники обсуждают корректность изложения теории категорий, в частности, определение естественных преобразований и функторов.
- Высказываются различные мнения о полезности теории категорий в компьютерных науках и программировании как набора абстрактных моделей и инструментов.
- Отмечается стиль изложения материала, который некоторые находят слишком неформальным для технического текста, с обилием иллюстраций и аналогий.
- Обсуждается авторство и история публикации материала, а также его неоднократное появление в топе Hacker News.
Type Theory and Functional Programming (1999) [pdf]
Конструктивная теория типов объединяет логику и программирование в единую систему, где разработка и верификация программ происходят одновременно. Она представляет собой функциональный язык с уникальными чертами: тотальностью функций, зависимыми типами, позволяющими тип результата определять значением аргумента, а также продвинутыми модулями с логическими утверждениями в интерфейсах. Программы можно извлекать непосредственно из доказательств, что открывает возможности для автоматизированного создания корректного кода.
Книга служит введением и углублённым курсом, начиная с основ логики, λ-исчисления и функционального программирования, затем детально представляет систему теории типов с примерами. Она также критически анализирует предложения по расширению системы, такие как подавление свидетельствующей информации в подтипах или добавление коиндуктивных типов для работы с бесконечными потоками, сохраняя при этом свойства завершаемости. Практическая реализация системы помогла прояснить тонкие аспекты, например роль предположений.
Комментарии (66)
- Обсуждается перспектива широкого внедрения функционального программирования благодаря его теоретической основе, аналогично устойчивости реляционных баз данных и SQL.
- Поднимается вопрос о применении теории типов в императивных языках, с примерами из Rust и C++, где типы улучшают безопасность и выразительность.
- Высказывается критика функционального программирования как сложного и непрактичного, порождающего "ужасный код", в противовес его восприятию как более безопасного и элегантного.
- Участники делятся рекомендациями по литературе для изучения темы (TTOP, Types and Programming Languages) и отмечают её сложность, но ценность.
- Отмечается, что чистые функциональные языки непопулярны (TIOBE), но их идеи (лямбды, монады) проникают в мейнстрим (например, для асинхронного программирования).
`std::flip`
std::flip — это малоизвестная утилита из стандартной библиотеки C++ в заголовке <functional>, которая принимает вызываемый объект и возвращает его аналог с обратным порядком параметров. Она особенно полезна для переворачивания предикатов, например, чтобы проверить, является ли один узел дерева потомком другого, создав функцию is_descendant_of через std::flip(is_ancestor_of), что зеркально отражает логику предка.
Идея пришла из функционального программирования: аналоги есть в Haskell, PureScript, OCaml и других языках, а также в библиотеках вроде Boost.Hana и Ramda. В сочетании с std::not_fn она позволяет выражать все операторы сравнения через один базовый предикат, упрощая generic-алгоритмы — например, проверку обратной сортировки или реализацию upper_bound через lower_bound. Это элегантный инструмент для сокращения кода и повышения его переиспользуемости.
Комментарии (97)
- Сомнения в существовании
std::flipв C++ и критика его потенциального имени и нишевости. - Обсуждение недостающих для функционального стиля элементов C++: сопоставление с образцом и унифицированный синтаксис вызова.
- Критика сложности предложенной реализации
flipи её потенциальной бесполезности или опасности. - Дебаты о практической полезности функции
flipи её аналогов в других языках. - Обсуждение проблем представления геоданных и применимости
flipкак потенциального источника ошибок.
Knotty: A domain-specific language for knitting patterns 🔥 Горячее
Knotty — это предметно-ориентированный язык (DSL) для описания вязальных паттернов, реализованный в Racket. Он позволяет программировать схемы вязания, используя код вместо традиционных графических или текстовых инструкций. Основная идея заключается в автоматизации создания сложных узоров, что упрощает работу дизайнеров и энтузиастов.
Язык предоставляет модули для ввода-вывода данных, примеры кода и справочник, облегчая освоение и применение. Knotty демонстрирует, как нишевые DSL могут решать специализированные задачи, сочетая программирование с ремеслом.
Комментарии (49)
- Обсуждение исторических связей между программируемыми ткацкими станками (Жаккардовы машины) и современными компьютерами.
- Восхищение проектом как примером элегантного и практичного инженерного решения, продолжающего исторические традиции.
- Размышления о параллелях между вязанием и функциональным программированием, где физические ограничения сравниваются с системой типов.
- Интерес к современным DIY-станкам для вязания и возможностям автоматизации создания узоров с помощью ИИ.
- Шуточные комментарии об ожиданиях увидеть что-то другое (например, терминальный эмулятор) и отсылки к поп-культуре.
The Lambda Calculus (2023)
Лямбда-исчисление — это минималистичный формализм для представления функций и их применения, основанный на двух ключевых операциях: абстракции (создание функции через λx.M) и аппликации (применение функции к аргументу, записывается как MN). Его синтаксис крайне лаконичен, но он позволяет выражать любые вычисления через правила подстановки.
Центральное правило — β-редукция: (λx.M)N сводится к M[x:=N], то есть замене всех вхождений x в M на N. Это отражает идею вычисления через подстановку аргумента. Например, (λx.x²−2x+5)2 даёт 2²−2·2+5=5. Важно, что функции здесь трактуются не как множества пар, а как правила преобразования, что делает теорию неэкстенсиональной.
Лямбда-исчисление легло в основу теории вычислений, логики и программирования, демонстрируя, как простые синтаксические правила могут порождать богатую семантику. Оно показывает, что даже в такой скудной нотации можно выразить всю вычислимую математику.
Комментарии (12)
- Обсуждаются различные подходы к изучению и объяснению λ-исчисления, включая книги, статьи и аналогии с языками программирования.
- Участники делятся ресурсами для изучения темы, такими как книги "Type Theory and Formal Proof" и блог-пост с введением в λ-исчисление на Python.
- Поднимаются вопросы о сложности восприятия материала и даются советы, как преодолеть первоначальное непонимание.
- Высказываются идеи о применении λ-исчисления и комбинаторной логики как общей семантической основы для языков и систем.
- Обсуждается связь λ-исчисления с формальными доказательствами и системами типов, а также его практическая полезность.
What is algebraic about algebraic effects?
В программировании «алгебра» — это не школьные уравнения, а способ давать коду строгую структуру через свойства операций. Если обычная композиция объектов ограничивается «они реализуют один интерфейс», то алгебраическая композиция требует, чтобы набор данных и операций удовлетворял фиксированным законам: замкнутость, ассоциативность, единица, обратные элементы и т.д. Набор «целые числа + сложение» образует группу, потому что все четыре закона выполняются; код, в котором сложение строк вдруг выдаёт число, группой не является.
Именно этим объясняется «алгебраичность» Algebraic Effects: набор эффектов и обработчиков строится как свободная алгебра с заданной сигнатурой операций, а значит любая программа сводится к выражению, подчиняющемуся строгим законам переписывания. Практический выигрыш — возможность комбинировать и вложенно перехватывать эффекты без «callback-ада», потому что поведение заранее ограничено алгебраическими законами.
Комментарии (32)
- Обсуждается различие между "алгебраическими" в контексте типов данных и эффектов, подчеркивая, что последние связаны с алгеброй монад, а не просто наличием уравнений.
- Участники предлагают ресурсы для изучения темы (статья "What is algebraic about algebraic effects and handlers?") и проводят аналогии с булевой алгеброй для лучшего понимания.
- Отмечается сложность терминологии и необходимость перевода абстрактных математических концепций (операции над типами/эффектами) в более доступные для программистов термины.
- Поднимается вопрос о синтаксическом представлении алгебраических типов (сумм и произведений) в разных языках программирования (OCaml, Lean).
- Обсуждается практическое применение алгебраических операторов на примере пакета Algebra of Graphics для Julia.
How to stop functional programming (2016)
Статья иронично описывает ситуацию, когда разработчика заставляют отказаться от функционального программирования из-за жалоб коллег. Менеджер принимает «техническое решение» запретить ФП, чтобы избежать конфликтов, и автор пытается следовать указанию, намеренно добавляя побочные эффекты в код.
Пример с функцией userCoworkers показывает, как чистый код постепенно усложняется: сначала добавляется мутабельная коллекция, затем логирование. Юмор в том, что даже с побочными эффектами метод остаётся чистым снаружи, а задание «просто вернуть число» ставит под вопрос, как вообще избежать функциональных подходов. Финальный совет — спросить у менеджера, как сложить числа без чистых функций — подчёркивает абсурдность таких запретов.
Комментарии (62)
- Важность написания читаемого кода с учетом целевой аудитории (уровня навыков читателей) и необходимости согласования стиля в команде.
- Критика восприятия функционального программирования (ФП) как исключительно сложного, с указанием, что проблема часто в неидиоматичном коде, излишне длинных цепочках методов или специфических особенностях языков (например, Haskell), а не в ФП как парадигме.
- Необходимость обучения, код-ревью и парного программирования для внедрения сложных концепций и выравнивания уровня команды, особенно при использовании нишевых языков (например, Scala).
- Споры о балансе между простотой (для младших разработчиков) и продвинутыми практиками, где чрезмерное упрощение может привести к посредственному коду, а сложность — к проблемам читаемости.
- Роль менеджмента в разрешении конфликтов через технические решения, которые могут ограничивать инструменты или стили (например, запрет ФП), иногда без глубокого понимания причин проблемы.
Show HN: WeUseElixir - Elixir project directory
Каталог WeUseElixir собирает реальные примеры использования языка Elixir в продакшене, демонстрируя разнообразие его применения — от библиотек до крупных компаний. Здесь можно найти такие известные инструменты, как Oban для обработки фоновых заданий, Absinthe для работы с GraphQL и Flop для пагинации в Ecto.
Среди компаний, применяющих Elixir, — PepsiCo, платформа для стриминга концертов VEEPS и сервис удалённой работы Remote. Каталог помогает разработчикам находить вдохновение, инструменты и потенциальных работодателей, подчёркивая практическую ценность экосистемы Elixir.
Комментарии (50)
- Участники высоко оценивают язык Elixir, его подход к функциональному программированию, параллелизму и сообщество, отмечая его эффективность для проектов любого масштаба.
- Были предложения по улучшению каталога WeUseElixir: добавить фильтрацию по типам проектов, разрешить добавлять компании без регистрации и исправлять данные о стеках технологий.
- Обсуждались технические аспекты: преимущества BEAM (виртуальной машины Erlang) для отказоустойчивости, продуктивность фреймворка Phoenix LiveView и варианты интеграции с клиентским состоянием.
- Участники поделились известными компаниями и проектами, использующими Elixir (например, Plausible Analytics, Supabase, ElectricSQL), и другими подобными каталогами.
- Задан вопрос о выборе между языками экосистемы BEAM: Erlang (для опытных команд), Elixir (общего назначения) и Gleam (строгая типизация).
Комментарии (39)
- Критика стиля и содержания поста Вольфрама: много визуализаций без ясной цели, изобретение непонятного термина "ruliology", самопрезентация автора.
- Положительные отзывы: пост как хорошее введение в лямбда-исчисление, демонстрация ценности визуализации даже абстрактных концепций, стимулирующий и ясный стиль написания.
- Сравнение с другими систематическими работами: упоминание функционального аналога "занятого бобра" (A333479) и вопроса о простоте анализа по сравнению с машинами Тьюринга.
- Обсуждение целевой аудитории: пост написан не для инженеров-программистов, а для физиков и математиков, изучающих вычисления.
- Упоминание концепций Вольфрама: "ruliad" (вселенная как программа) и "ruliology" как их изучение, переоткрытие тезиса Чёрча-Тьюринга.
My first impressions of Gleam
Первые впечатления о Gleam
Ищу новый язык для изучения и выбрал Gleam — это «эликсир» со статической типизацией.
Чтобы понять, подойдёт ли он, решил переписать старый pet-project: парсер логов AIM 1999-2007 гг.
Формат простейших логов — plain-text:
Session Start (DumbAIMScreenName:Jane): Mon Sep 12 18:44:17 2005
[18:44] Jane: hi
[18:55] Me: hey whats up
Session Close (Jane): Mon Sep 12 18:56:02 2005
Цель: вытащить только тексты сообщений, потом добавить метаданные и веб-интерфейс.
CLI-аргументы
Стандартной библиотеки для argv нет.
Сторонний пакет argv решает задачу в 4 строки:
case argv.load().arguments {
[path] -> io.println("arg: " <> path)
_ -> io.println("Usage: gleam run <dir>")
}
Компиляция
gleam build собирает проект, но исполняемого файла не создаёт.
В build/dev/erlang/... появляются .beam-файлы — байт-код для BEAM-ВМ.
Запускать удобнее через gleam run.
Парсер
Начал с теста: функция принимает многострочный текст и возвращает список сообщений.
plaintext_logs.parse(log)
|> should.equal(["hi", "hey whats up"])
Дальше — добавлю структуру Message {time, author, text} и разберу XML/HTML-логи.
Комментарии (72)
- Gleam: маленький, строго типизированный FP-язык на BEAM; хвалят простоту, TEA/actor-модель и устойчивость к ошибкам.
- Главный минус — нет трейтов/интерфейсов и приходится подключать внешнюю библиотеку для акторов.
- Кто-то хочет LLVM-бекенд вместо BEAM, но большинство считают BEAM лёгким, масштабируемым и почти незаменимым для конкурентных задач.
- Парсинг: комбинаторы удобны, но медленны без оптимизирующего компилятора; в Gleam чаще используют pattern-matching и пакет splitter.
- Экосистема молода: библиотек мало, LLM плохо знают язык, но interop с Erlang/Elixir уже работает.
Death to type classes
Смерть классам типов!
Числа пасутся? Функторы поют? Нет — это ложь элит. Backpack вместо классов: сигнатуры модулей, а не class.
signature Functor (Functor, map) where
data Functor a
map :: (a→b) → Functor a → Functor b
Реализация для Maybe:
module Functor.Maybe where
type Functor = Maybe
map f (Just x) = Just (f x)
map _ Nothing = Nothing
Cabal: сигнатуры в library, реализации в library impl. Подмена через mixins:
mixins: death (Functor.Sig as Functor.Maybe)
Можно много реализаций в одном проекте: List, Maybe, IO — всё живёт рядом, без {-# LANGUAGE RebindableSyntax #-} не обойтись.
Итог: вместо классов — модули, вместо инстансов — реализации. Композиция через Backpack, а не instance.
Комментарии (67)
- Обсуждение сфокусировано на статье о Haskell, которая исследует альтернативный способ структурирования кода с использованием расширения Backpack, малоизвестного даже в сообществе Haskell.
- Многие комментаторы отмечают, что материал сложен для понимания и требует глубоких знаний как Haskell, так и модульной системы OCaml, что делает его недоступным для широкой аудитории.
- Поднимается вопрос о целевой аудитории: уместно ли публиковать узкоспециализированные посты на платформе с разнородной аудиторией и следует ли ожидать критики от тех, кто не входит в целевую группу.
- Одна из центральных тем — сравнение подхода Backpack с более традиционными для Haskell концепциями, такими как тип-классы, и обсуждение их преимуществ и недостатков.
- Некоторые участники дискуссии выражают скептицизм по поводу практической полезности Backpack, отмечая, что он почти не используется в реальных проектах.
- В комментариях также встречаются шутки и отсылки к мемам о сложности функционального программирования и его концепций (монад, функторов).
Rust: A quest for performant, reliable software [video]
- YouTube
О платформе, прессе, авторском праве, контактах, авторах, рекламе, разработчикам.
Условия, конфиденциальность, безопасность, как работает YouTube, тест новых функций, NFL Sunday Ticket.
© 2025 Google LLC
Комментарии (63)
- Участники спорят, «победил» ли Rust: кто-то считает, что язык уже получил признание индустрии и государства, другие — что говорить о победе рано.
- Ключевой аргумент «за» — широкое распространение знаний: после увольнений в Mozilla бывшие разработчики разошлись по большим компаниям, и ни одна корпорация теперь не контролирует язык.
- Главное преимущество Rust называют не безопасность, а юзабилити: понятные ошибки компилятора, привычный синтаксис и рабочие инструменты сделали «функциональное программирование массовым».
- Качество экосистемы вызывает вопросы: 90 % опубликованных крейтов — заброшенные или низкокачественные, поиску мешает отсутствие курации на crates.io.
- Тем не менее число живых, аудированных и развивающихся библиотек растёт экспоненциально, а крупные проекты (ripgrep, wgpu, iroh) уже используются в продакшене.
Clojure's Solutions to the Expression Problem
Clojure и «проблема выражений»
- Проблема: добавлять новые типы данных и новые операции без перекомпиляции всего кода и без нарушения существующих вызовов.
- ООП-языки решают лишь половину: легко новый класс, трудно новый метод.
- Функциональные языки наоборот: легко новая функция, трудно новый вариант данных.
Как Clojure объединяет лучшее
-
Протоколы
- Описывают набор методов без привязки к типу.
- Реализуются для любого существующего класса поздно, «извне».
- Компилируются в обычный Java-интерфейс, быстрый вызов.
-
Мультиметоды
- Выбор реализации по произвольной функции-диспетчеру (тип, значение, метаданные).
- Позволяют «разрезать» иерархию по другим осям, не только по классу.
-
Records и types
defrecordсоздает неизменяемую структуру с заранее известными полями и автоматическим доступом по ключам как к карте.deftypeдаёт полный контроль, поля хранятся примитивно, без лишних обёрток.
-
reify
- «Анонимный класс» на Clojure: создаёт объект, реализующий нужные протоколы/интерфейсы, без отдельного файла.
Практический итог
- Новый тип →
defrecord/deftype+ реализация нужных протоколов. - Новая операция → добавляем метод в протокол и реализуем для всех существующих типов.
- Старый клиентский код не трогается, компиляция не требуется.
Комментарии (16)
- Протоколы в Clojure теперь умеют диспатчиться по метаданным, а не только по типу.
- Участники скучают по временам, когда посты о продуктивности были про REPL, функциональность, композабельность и иммутабельность.
- Поделились ссылкой на демонстрацию решения «expression problem» в Clojure.
- Люди недооценивают готовые решения вроде Datomic и вместо этого пишут свои костыли.
- Один из участников вспомнил, как в 2013–15 писал тесты на Clojure и расширял фреймворк для Selenium.
The Expression Problem and its solutions (2016)
Проблема выражений и её решения
Проблема выражений: нужно добавлять новые типы данных и новые операции без изменения старого кода.
В ООП-языках легко добавлять типы (наследование), но сложно — операции (менять интерфейс).
В функциональных языках наоборот: легко добавлять функции, сложно — варианты данных.
Пример на C++
Базовый класс Expr с двумя методами: Eval() и ToString().
Новый тип — просто новый класс-наследник.
Новая операция — правим базовый класс и все наследников, нарушая OCP.
Функциональный подход (Haskell)
Типы данных и функции разведены:
data Expr = Constant Double | BinaryPlus Expr Expr
eval (Constant x) = x
eval (BinaryPlus a b) = eval a + eval b
Добавить операцию легко: пишем новую функцию.
Добавить вариант Expr — правим сам тип и все функции, pattern-match’и которых его затрагивают.
Как быть
- Визитор (ООП) — двойная диспетчеризация, но код всё равно растёт.
- Мультиметоды (CLOS, Clojure) — выбор по типу всех аргументов, код не трогается.
- Type-class / протоколы (Haskell, Clojure) — «открытые» функции, реализуемые вне исходного модуля.
- Tagless-final / finally-tagless — выразить язык как набор операций, интерпретаторы добавляются без изменения AST.
Итог: ни один стиль не побеждает; выбираем язык и технику, которая даёт нужную сторону расширяемости.
Комментарии (69)
- Суть проблемы: нужно добавлять новые типы данных и новые операции без переписывания старого кода и без потери статической безопасности.
- Классический подход: O·T матрица — каждая операция реализуется для каждого типа; при росте O или T взрывается boilerplate.
- Языковые решения:
– трейты/impl в Rust, typeclass в Haskell, протоколы в Swift позволяют добавлять операции «сбоку»;
– enum (суммарные типы) упрощают добавление операций, но затрудняют новые варианты данных. - Продвинутые техники: tagless-final, object algebras, data types à la carte, multiple dispatch, visitor-pattern.
- Ограничения: «сиротское правило», отсутствие виртуальных extension-методов, необходимость заранее знать все комбинации при раздельной компиляции.
- Вывод: идеального языка нет; выбор зависит от того, что важнее — расширять типы или операции — и насколько нужна статическая типизация.
Evolving the OCaml Programming Language (2025) [pdf]
- Эволюция OCaml – Ashoka Univ, сент 2025 [pdf][key]
- Авто-проверка реплиц. типов – NUS, авг 2025 [pdf][key]
- AI-инструменты для исследований – IIT Madras, июль 2025 [pdf][key]
- Параллельный рантайм OCaml – Chalmers, май 2025 [pdf][key]
- Авто-проверка реплиц. типов – WG 2.8, май 2025 [pdf][key]
- Параллельный OCaml 5 – Bloomberg, мар 2025 [pdf][key]
- Параллельный OCaml 5 – IIT Gandhinagar, мар 2025 [pdf][key]
- Параллельный OCaml 5 (ч.1) – PACE Lab, фев 2025 [pdf][key]
- Безопасность памяти и ЯП – Schaeffler @ IITM, фев 2025 [pdf][key]
- Мини-ОС через Unikernels – Daekin–IITM, янв 2025 [pdf][key]
- Безопасные Unikernels с аппаратной поддержкой – CAIR DRDO, ноя 2024 [pdf][key]
- Параллельный OCaml 5 – Meta London, сен 2024 [pdf][key]
- Зачем OCaml? – Rezilyens, авг 2024 [pdf][key]
- Эффекты и конкурентность – Chalmers, май 2024 [pdf][key]
- Безопасность функциональных программ – WG 2.8, апр 2024 [pdf][key]
- Композиция библиотек конкурентности – EHOP, июл 2023 [pdf][key]
- Сливаемые реплиц. типы – Collège de France, апр 2023 [pdf][key][видео]
- OCaml 5.0 – OCaml Workshop, сен 2022 [pdf][key]
- Ретрофит конкурентности – ICFP keynote, сен 2022 [pdf][key][видео]
- Сертифицированные сливаемые типы – PLDI, июн 2022 [pdf][key][видео]
- Сертифицированные сливаемые типы – Nomadic Labs, апр 2022 [pdf][key][видео]
- Параллелизм в OCaml – Marigold, дек 2021
- Эффекты в OCaml 5 – Huawei STW, окт 2021 [pdf][key]
- Эффекты в OCaml – SimCorp, сен 2021 [pdf][key]
- Параллелизм в OCaml – SimCorp, сен 2021 [pdf][key]
- ParaFuzz: фаззинг многопоточных программ – Dagstuhl, 2021
Комментарии (30)
- Доклад — субъективный взгляд на 10-летнюю эволюцию OCaml; цель — убрать мистику вокруг разработки компилятора и заманить новых контрибьюторов.
- Главный бытовой pain-point: в стандартной библиотеке исключения — часть API, и они не типизированы, что подрывает «безопасность» языка.
- Выход — использовать Jane Street Core/Base (функции либо возвращают Result, либо помечены _exn), но большинство проектов всё ещё живёт на обычной Stdlib.
- «Большие» альтернативы Stdlib (Core, Base) существуют, но их значение часто преувеличено; официальная библиотека за последние годы всё-таки подросла полезными функциями.
- Новичкам в компиляторщине советуют начинать с мелких багов и мини-Pull Request’ов, а не пытаться сразу «съесть слона».
Anonymous recursive functions in Racket
Репозиторий показывает, как в Racket писать анонимные рекурсивные функции без letrec и имен.
Ключевая идея — Y-комбинатор: лямбда получает себя как аргумент и вызывает его для следующего шага.
(define Y
(λ (f)
((λ (x) (x x))
(λ (x) (f (λ (a) ((x x) a)))))))
((Y (λ (fact)
(λ (n)
(if (zero? n) 1 (* n (fact (sub1 n)))))))
5) ; 120
Такой приём работает для любой рекурсии: факториал, fib, обход списков и т.д.
Комментарии (37)
- Обсуждение началось с примера анонимной рекурсии на Racket; оказалось, что код совместим с любым R6RS-Scheme, включая проект scheme-rs.
- Участники сравнили подходы: в Clojure нужен явный
recur, в Racket хвостовые вызовы оптимизируются автоматически. - Кто-то спросил, стоит ли брать Racket для повторного изучения ФП; советуют почитать «zen of Racket» и быть готовым к узкой, но мощной экосистеме.
- Появились порты идеи на Python и Go (через Y-комбинатор), но часть людей предпочла бы обычный цикл для отладки.
- Сообщество предупреждает: в нишевых языках придётся уметь докручивать библиотеки «с нуля» и держать редких специалистов.
Optimising for maintainability – Gleam in production at Strand
Задача
Лондонское агентство Strand ведёт сотни маркетинг-проектов в год. Финансовый учёт раньше велся вручную через таблицы. В 2020 г. команда из трёх разработчиков запустила прототип финансовой системы; он быстро стал критически важным, и потребовалось обеспечить надёжность и простоту поддержки при минимальных ресурсах.
Решение
Strand выбрал язык Gleam на платформе BEAM:
- Безопасность: чистый код не падает; сбои внешних сервисов изолируются лёгкими процессами.
- Практичность: язык мал, однозначен, осваивается за день; доступны 40 лет библиотек Erlang/Elixir.
- Опыт разработки: единый пакет с форматтером, автодополнением и понятными ошибками; сборка мгновенная.
Система обрабатывает курсы валют, синхронизирует данные с внешним ПО и продолжает расти без роста технического долга.
Комментарии (20)
- Вероятно, языки вроде Gleam выиграют у ИИ благодаря строгой типизации и быстрой обратной связи.
- Некоторые новички застревают в «абстракционном аду» и советуют сначала освоить Erlang/Elixir.
- Производственные истории успеха Gleam на BEAM вдохновляют.
- Однофайловые исполняемые файлы можно собрать через компиляцию в JavaScript + Node SEA или Burrito, но полноценный VM-free релиз пока нет.
- Качество примеров и стабильность языка становятся важнее их количества для LLM.
- Чистые, референциально прозрачные языки удобны ИИ для параллельного тестирования блоков кода.
I'm working on implementing a programming language all my own
Я пишу свой язык Baba Yaga — чисто из любви к эстетике кода. Рабочий интерпретатор есть, но полноценной среды пока нет; планирую браузерный «бесконечный холст» в духе Smalltalk.
Язык — эксперимент ради удовольствия: неизменяемость, функциональный стиль, минимальный синтаксис, базовые «батарейки». Это Toki Pona для Haskell.
Синтаксис и типы
Объявления переменных и функций одинаковы; скобки не нужны, каррирование бесплатно.
transport : "Chicken House"
add : x y -> x + y
add5 : add 5
Базовые неизменяемые типы: Int, Float, String, Bool, List, Table.
numbers : [1, 2, 3]
person : {name: "Lucy", age: 23}
Типы можно указать явно; без аннотаций всё проверяется только во время вызова.
Управление потоком
Единственный способ — when (pattern matching). Нет if/else или switch.
describe : x ->
when x is
0 then "Zero"
_ then "Something else"
processUser : user ->
when user is
{name: n, age: a, active: true} -> "Adult: " .. n
_ -> "Unknown"
Пример: жизнь Конвея уже работает.
Комментарии (36)
- Участники делятся опытом создания языков: @codr7 показал фреймворк Shi, @aldousd666 рассказал о своих интерпретаторах, начавшихся в 2006-м.
- Разгорелся спор о синтаксисе: одни хвалят «:» для присваивания, другие защищают «=» как уже устоявшийся символ.
- Кто-то предлагает отказаться от точек с запятой, кто-то считает их полезным разделителем.
- Обсуждаются математические корни обозначений: одни считают «=» сравнением, другие — утверждением.
- Предлагаются альтернативы: «:=», «<-», «=>», а также идея вызывать функции в порядке чтения.
Show HN: Using Common Lisp from Inside the Browser
Web Embeddable Common Lisp (WECL) — запуск Common Lisp в браузере через WebAssembly.
Проект экспериментальный; API нестабильны, баг-репорты не принимаются.
Исходники: fossil.turtleware.eu/wecl.
Быстрый старт
Подключите boot.js и wecl.js, пишите код в <script type="text/common-lisp">.
Пример считает обратный отсчёт и выводит «BOOM!».
Демо: easy.html | easy.lisp
JS-FFI
Набор макросов для вызова JavaScript из Lisp:
| макрос | назначение |
|---|---|
define-js-variable |
переменная, выражение подставляется каждый раз |
define-js-object |
объект, сохраняется в хранилище |
define-js-function |
функция |
define-js-method |
метод объекта |
define-js-getter/setter/accessor |
чтение/запись поля |
define-js-script |
шаблон JS-выражения |
define-js-callback |
Lisp-функция, вызываемая из JS |
lambda-js-callback |
анонимный callback |
Типы аргументов: :object, :js-ref, :fixnum, :symbol, :string, :null.
Emacs-интеграция
LIME/SLUG позволяет REPL и отладку прямо из Emacs.
Инъекция в любой сайт
Загрузка WECL в произвольную страницу через bookmarklet или расширение.
Ограничения
- Размер wasm-модуля ≈ 10 МБ (кешируется).
- Нет потоков, FFI неполный, производительность средняя.
Финансирование
Разработка поддерживается подписчиками Patreon.
Комментарии (24)
- Обсуждение началось с альтернативной истории: если бы JS изначально был похож на Lisp, возможно, функциональное программирование стало бы мейнстримом раньше.
- Участники отмечают, что WebAssembly и Gambit-JS уже позволяют писать на Scheme/Common Lisp в браузере, улучшая DX и переиспользование кода.
- Некоторые сомневаются, что Lisp автоматически «функциональнее» JS, и подчеркивают, что императивный стиль часто естественнее.
- Поднимаются риски: рост размера рантайма, проблемы на медленных каналах и возможное доминирование Microsoft, если бы IE/VBScript победил.
Left to Right Programming 🔥 Горячее 💬 Длинная дискуссия
Программа должна оставаться валидной по мере набора.
Python-списковые включения плохи: пока вы не допишете for line in text.splitlines(), редактор не знает тип line, не может подсказать split() и даже не понимает, существует ли переменная.
В Rust text.lines().map(|line| line.split_whitespace()) строится слева-направо: сразу после line. доступны методы, и код всё время «жив».
Принцип progressive disclosure: сложность появляется ровно тогда, когда нужна. В C функции для FILE* начинаются на f; вводишь f и видишь сотни вариантов, не понимая, какой подходит. В идеале file. показал бы read, close и т. д. прямо во время набора.
Python и JS:
map(len, text.split())
— неясно, как называется длина (len, length, size?).
text.split(" ").map(w => w.length)
— length и map подсказываются сразу после точки.
С ростом логики читаемость падает:
len(list(filter(lambda line: all([abs(x) >= 1 and abs(x) <= 3 for x in line]) ...
Комментарии (323)
- Обсуждение вращается вокруг идеи «слева-направо» синтаксиса: сначала контекст (объект, коллекция), потом действие, чтобы IDE могла мгновенно подсказывать.
- Критикуют Python- comprehensions и вложенные вызовы за «вправо-налево» порядок, мешающий автокомплиту и чтению.
- Многие хотят pipe-оператор (|>) или uniform call syntax, как в F#, Elixir, Nim, чтобы цепочки выглядели
data |> filter(...) |> map(...). - Противники считают, что язык не должен подстраиваться под автокомплит, а сложные цепочки лучше выносить в переменные или использовать методы.
- Примеры из SQL, JS/TS импортов, Rust for-loops и C# LINQ показывают ту же проблему: сначала надо указать, что ты хочешь получить, а потом — откуда.
Clojure Async Flow Guide
Быстрый старт
Библиотека flow отделяет бизнес-логику от развёртывания: топологии, исполнения, обмена сообщениями, жизненного цикла, мониторинга и обработки ошибок.
step-fn и процессы
Логика описывается функциями step-fn, которые flow заворачивает в процессы, крутящиеся в цикле. step-fn не работают с каналами напрямую и не хранят состояние, поэтому легко тестируются и переиспользуются.
step-fn имеет четыре арности:
describe (step-fn) → descriptor
Возвращает статическое описание :params, :ins, :outs — карты имя → документация. Имена входов и выходов не должны пересекаться.
{:params {:size "Максимальный размер"}
:ins {:in "Входной канал"}
:outs {:out "Выходной канал"}}
init (step-fn arg-map) → init-state
Один раз вызывается при старте процесса; превращает параметры из flow-def в начальное состояние.
transition (step-fn state transition) → state'
Вызывается при переходах жизненного цикла (::flow/start, ::flow/stop, ::flow/pause, ::flow/resume). Используется для управления внешними ресурсами.
transform (step-fn state input msg) → [state' {out-id [msgs]}]
Вызывается для каждого входящего сообщения. Возвращает новое состояние и карту выходных сообщений. Выход может быть пустым, но каждое сообщение — не nil. Исключения логируются в :error-chan.
Состояние процесса
Карта с любыми ключами. Дополнительно:
::flow/pid— идентификатор процесса::flow/in-ports,::flow/out-ports— карты cid → внешний канал (создаётся вinit)::flow/input-filter— предикат cid для фильтрации входных каналов
Хелперы
lift*->step— изf(x) → collделает step-fn с одним входом и выходомlift1->step— то же, ноf(x) → single-valuemap->step— из карты с ключами:describe,:init,:transition,:transformстроит step-fn
Запуск процесса
Функция process принимает step-fn и опции:
::workload—:mixed,:io,:compute:compute-timeout-ms— таймаут для:compute(по умолчанию 5000 мс)
Комментарии (74)
- Участники обсуждают, жив ли Clojure: сообщество стабильно, но менее хайповое; NuBank расширяет core-команду и нанимает Developer Advocate.
- core.async.flow предлагает декларативный, фиксированный граф каналов для «структурированной конкурентности»; ошибки и паузы можно отслеживать, но изменять топологию на лету пока нельзя.
- Сравнивают с GenStage (Elixir), Manifold, Trio и missionary/electric; можно использовать как OS-, так и green-потоки.
- JVM-тулчейн вызывает у новичков страх, но Leiningen/deps.edn упрощают работу, а отладка всё же возможна.
- Clojure-окосистема активно развивается: Babashka, XTDB, Dyna3 и другие проекты; язык недавно получил мажорный релиз и готовится к виртуальным потокам JVM.
Typechecker Zoo
Проект «Zoo» — мини-реализации самых влиятельных статических систем типов последних 50 лет. Начнём с простых и дойдём до зависимых типов. Всё пишем на Rust — просто потому что удобно и забавно строить чисто функциональные языки на не-функциональном.
Это не учебник, а выходные развлечение. За теорией и доказательствами смотрите TAPL, ATTAPL, PFPL и оригинальные статьи (ссылки в приложении). Здесь же — грязные детали реализации: структуры данных, AST, логика, всё, что можно осилить за уик-энд.
Код — идиоматичный Rust с полноценным парсером и тестами (lalrpop, logos, ariadne). Примеры урезаны, но понятнее продакшен-реализаций. Парсинг и MLIR считаем решёнными задачами, поэтому не фокусируемся на них.
Четыре «зверька»:
- Algorithm W (775 строк) — классический Hindley–Milner, полиморфный λ-исчисление.
- System F (1090 строк) — второе λ-исчисление, параметрический полиморфизм, Mini-OCaml.
- System F-ω (3196 строк) — высшие рода, паттерн-матчинг, дататипы, Haskell-lite.
- Calculus of Constructions (6000 строк) — иерархия универсумов, индуктивные типы, крошечный Lean.
MIT-лицензия, хобби-проект. Нашли опечатку — присылайте pull-request.
Комментарии (24)
- Пользователи хвалят Elaboration Zoo как полезный ресурс для изучения нормализации по вычислению и вывода неявных переменных.
- Просят аналогичный «зоопарк» для линейных типов и предлагают добавить быстрый вариант Hindley–Milner из OCaml.
- Автору советуют включить тёмную тему для блоков кода и рассмотреть простой однонаправленный type-checker в духе Featherweight Java.
- Уточняют, что присутствие индуктивных типов делает реализацию ближе к CIC, но Lean всё же сильнее за счёт аксиомы выбора.
- Картинки с животными вызывают путаницу; большинство считают их просто AI-орнаментом без смысловой нагрузки.
Why tail-recursive functions are loops
Хвостовая рекурсия превращает рекурсию в цикл: компилятор заменяет вызов на безусловный jmp, поэтому стек не растёт.
Обычная рекурсия кладёт промежуточные значения в стек, тратит O(n) памяти и вытесняет кэш.
Цикл же держит результат в аккумуляторе, использует O(1) памяти и линейное время.
Ключевое правило хвостовой рекурсии:
вызов должен быть последним выражением функции. Тогда компилятор может выбросить текущий фрейм и передать управление напрямую.
Пример суммы списка
Обычная версия:
(define (sum l)
(if (empty? l) 0
(+ (first l) (sum (rest l)))))
Хвостовая версия:
(define (sum l acc)
(if (empty? l) acc
(sum (rest l) (+ acc (first l)))))
Аргументы l и acc перезаписываются «на месте», как переменные цикла.
Упражнение 1 — счётчик чётных/нечётных:
(define (even-odd l [e 0] [o 0])
(if (empty? l) (cons e o)
(let ([x (first l)])
(if (even? x)
(even-odd (rest l) (add1 e) o)
(even-odd (rest l) e (add1 o))))))
Упражнение 2 — сглаживание дерева:
используйте аккумулятор-список и обход в обратном порядке, чтобы сохранить хвостовой вызов.
Комментарии (121)
- Теоретически хвостовая рекурсия и циклы эквивалентны: любую хвостовую рекурсию можно превратить в цикл (и наоборот), но взаимно-рекурсивные функции требуют дополнительной работы.
- На практике циклы чаще проще для чтения и не ломают стек, тогда как хвостовая рекурсия нуждается в оптимизации (TCO), которую не все языки поддерживают (Python, V8 её нет).
- Некоторые языки (Scala, Clojure, F#) дают компромиссные конструкции (@tailrec, recur), сохраняющие функциональный стиль и гарантирующие отсутствие переполнения стека.
- Вместо явной хвостовой рекурсии часто достаточно высокоуровневых комбинаторов вроде fold/map, но они не всегда позволяют досрочный выход и могут расходовать O(N) памяти.
- Участники сходятся во мнении: владеть обоими подходами полезно, выбор зависит от языка, задачи и привычек команды.