Freer Monads, More Extensible Effects (2015) [pdf]
Авторы представляют рациональную реконструкцию расширяемых эффектов как альтернативы трансформерам монад. Они показывают, как свободные монады и расширяемые эффекты возникают из постепенного абстрагирования шаблонного кода при вычислениях с эффектами. Этот процесс приводит к "более свободным монадам" (freer monads), которые строятся без ограничения Functor. Продолжение в таких монадах может быть представлено эффективной выровненной по типам структурой данных, что позволяет создать библиотеку расширяемых эффектов, которая не только понятнее, но и быстрее предыдущих реализаций.
Авторы демонстрируют три удивительно простых применения: недетерминизм с обязательным выбором (LogicT), перехват исключений IO в присутствии других эффектов и полуавтоматическое управление файловыми дескрипторами через монадические регионы. Используется новый вид "ленивости", основанный на левом Kan расширении. Подход позволяет легко добавлять, комбинировать и инкапсулировать эффекты, при этом обеспечивая лучшую производительность по сравнению с MTL и другими библиотеками обработчиков эффектов.
Комментарии (17)
- Рекомендация изучить работы О. Киселёва (особенно по системам эффектов) за ясность изложения и педагогический подход.
- Упомянута библиотека
eff, но отмечено, что она не вышла в Hackage/продакшн; предложены альтернативы: Polysemy, effectful, Bluefin. - Обсуждение расширено на другие языки: Koka и Effekt (полноценные языки с нулевыми затратами на эффекты), effect-ts (самый популярный в TypeScript).
- Указано, что реализация систем эффектов требует компромисса между сложностью типов, шаблонным кодом, производительностью, обработкой высших порядков и безопасностью.
Algebraic Effects in Practice with Flix
Алгебраические эффекты на практике в Flix
Алгебраические эффекты — уже не академия. Это рабочий инструмент, который сегодня делает код:
- Тестируемым — «что» отделено от «как»; mock-и и DI не нужны.
- Прозрачным — сигнатура функции сразу показывает все побочные действия (IO, сеть, исключения).
- Гибким — async/await, корутины, backtracking реализуются обычными библиотеками, без изменения языка.
В отличие от монад, эффекты понятны без теории категорий и работают «из коробки» в языке Flix.
Мотивирующий пример
Без эффектов:
def calculateSalary(base, percent) -> float:
# может отправить письмо бабушке
С эффектами (Flix):
def calculateSalary(base: Float64, percent: Float64): Float64 \ {Email} =
...
Сигнатура не лжёт: любой вызов Email будет отслежен компилятором.
Обработчики эффектов (интуиция)
Эффект = операция + обработчик.
Код бросает операцию, обработчик решает, что с ней делать.
eff Ask { // объявляем эффект
pub def ask(): String
}
def greet(): String \ Ask = // используем
"Hello " + Ask.ask()
def main(): Unit = // обрабатываем
println(greet() with Ask {
def ask() = "World"
})
Реальный проект: рекомендательная система фильмов
Задача: достать данные из SQLite, вызвать внешний AI-сервис, кешировать результат, логировать.
Эффекты: Db, Http, Log, Cache.
def recommend(user: String): List[Movie] \ {Db, Http, Log, Cache} =
Cache.getOrElse(user,
for {
prefs <- Db.query(user)
_ <- Log.info("Prefs loaded")
recs <- Http.post("ai.example.com", prefs)
_ <- Log.info("AI answered")
_ <- Cache.put(user, recs)
} yield recs)
Тест: подменяем обработчики на in-memory реализации — никаких реальных запросов.
Куда дальше
- Попробовать онлайн: https://play.flix.dev
- Документация: https://doc.flix.dev
- Репозиторий: https://github.com/flix/flix
Flix ещё молод, но уже поддерживает эффекты, регионы, структурную конкурентность и Datalog.
Комментарии (42)
- Участники восторгаются идеей алгебраических эффектов: они проще монад, решают «цветную» проблему функций и позволяют гибко компоновать побочные эффекты.
- Примеры уже есть: экспериментальная реализация в OCaml 5, библиотеки Effect и Effectively для TypeScript, языки Koka, Effekt, Unison.
- Отличие от ОО-интерфейсов: эффекты явно указывают контекстные требования (Net, IO) и позволяют подменять реализацию динамически, а не статически.
- Критика: система всё равно «окрашивает» код, требует прокидывать эффекты через весь стек вызовов и пока выглядит академично/громоздко.
- Практические вопросы: как скрыть «дебажный» вывод, не нарушая типов, и можно ли обойтись без переписывания сигнатур каждой промежуточной функции.