世界を動かす技術を、日本語で。

なぜ代数的効果なのか?

2025年5月24日原文(antelang.org)

概要

  • Algebraic effects (効果ハンドラ)は、次世代言語で注目される機能
  • 例外やコルーチンなど多様な制御フローを ライブラリレベルで実現
  • 依存性注入やAPI設計の簡素化 にも有用
  • 副作用の抽象化 と柔軟な合成性を提供
  • 実用的な活用例と、ビジネスアプリにも役立つ理由を解説

Algebraic Effects(効果ハンドラ)とは何か、なぜ使うのか

  • Algebraic effects は、例外処理を「再開可能」にしたような仕組み
  • AnteやKoka、Effektなど多くの研究言語で 中核機能 として採用
  • 関数がどんな副作用を持つかを型で明示し、 柔軟にハンドリング可能
  • 例:effect SayMessage with say_message: Unit -> Unit のように宣言
  • handle ... | say_message () -> ... resume ()例外風に制御フローを記述

効果ハンドラの活用例

  • ジェネレータ、例外、コルーチン、非同期処理 などを、言語組み込みでなくライブラリとして実装可能
  • map関数のような高階関数も、 副作用に多態的 に対応できる
  • 例:map (input: Vec a) (f: a -> b can e): Vec b can e
    • fがどんな副作用eを持っていてもmapが同じ副作用を持つことを型で表現

例外の実装例

  • 例外=再開しない効果 として実装可能
    • effect Throw a with throw: a -> never_returns
    • handle ... | throw msg -> print msg で例外メッセージ処理

ジェネレータの実装例

  • effect Yield a with yield: a -> Unit を使い、 イテレーションやフィルタ もハンドラで記述
  • 例:yield_all_elements_of_vecfiltermy_for_each などを簡潔に実装

スケジューラやコルーチン

  • yield: Unit -> Unit などで 協調的マルチタスク も実現
  • Effektなどの例では、 複数の効果の合成も容易

抽象化としての効果ハンドラ

ビジネスアプリでの利点

  • 依存性注入 を副作用として抽象化可能
    • 例:データベースアクセスをeffect Database with query: String -> DbResponseとして抽象化
    • テスト時には モックDBロギングの差し替え も容易
  • 出力(print/log)も効果として抽象化
    • テスト時にprint出力を文字列として収集・検証が可能

柔軟なロギング

  • effect Log with log: LogLevel -> String -> Unitロギング出力の制御
  • ログレベルによる 出力のフィルタリング もハンドラで簡単に実装

よりクリーンなAPI設計

Contextの自動伝播

  • 典型的な「Contextオブジェクトの手渡し」を 効果として抽象化
  • effect Use a with get: Unit -> a set: a -> Unit状態の管理 も実現
  • 例:state (f: Unit -> a can Use s) (initial: s): a で初期状態を提供
  • これにより、 Contextの明示的な受け渡しが不要 となり、APIがシンプルに

状態管理の一般化

  • 内部状態を隠蔽しつつ、必要な操作だけをエクスポート
  • ライブラリ設計や抽象化の際に 内部実装の切り替えが容易

効果ハンドラの合成性・拡張性

  • 複数の効果(例:ロギング+DBアクセス+状態管理)が 簡単に合成可能
  • 他の副作用抽象(モナドなど)より 直感的で扱いやすい のが大きな強み

まとめ

  • Algebraic effectsは、 制御フロー・副作用・依存性注入 の抽象化に非常に有効
  • API設計やテスト容易性、拡張性 の向上に寄与
  • 今後のプログラミング言語設計で 主流となる可能性が高い技術

Hackerたちの意見

数年前にOCaml 5 alphaでプロトハッカーをやったことがあるんだけど、エフェクトを使ってね。楽しかったけど、その時のツールチェーンはちょっと使いづらかったんだ。今のはすごく似てる感じがするね。進展を楽しみにしてるよ。

OCaml 5.3の効果は、数年前よりかなりクリーンになったね(でもまだ型付けはされてないけど)。

アルジェブラ的エフェクトは、基本的に再開可能な例外だと思ってもらえればいいよ。ApplicativeErrorやMonadError[0]の型クラスを使うのと、実質的に何が違うの? > エフェクトを「投げる」には関数を呼び出す必要があって、その関数内ではそのエフェクトを使えると宣言しなきゃいけない。これはチェックされた例外と似てるね… これは上記の型クラスの一つで宣言されたエラータイプと、そのraiseErrorメソッドになるよ。 > そして、ハンドル式を使ってエフェクトを「キャッチ」できる(これをtry/catch式だと思って)。これがまさにこれらの型クラスが提供するもので、"handle式"はhandleErrorhandleErrorWithを使う(必要に応じて)。 > アルジェブラ的エフェクト(別名エフェクトハンドラ)は、非常に便利な新しい機能で、個人的には未来のプログラミング言語で大きな人気を得ると思ってるよ。「アルジェブラ的エフェクト」は未来のプログラミング言語だけじゃなく、実際に今のプログラミング言語でも人気があるんだ。

よくわからないけど、これは区切られた継続とも関係あるの?

ApplicativeErrorやMonadError[0]の型クラスを使うのと、実質的に何が違うの?静的な動作と動的な動作の違いだと思う。モナディックプログラミングでは、モナド内で関連するすべてのメソッドを実装しなきゃいけないけど、エフェクトを使うと、必要なところにエフェクトハンドラを動的にインストールできるんだ。二つのシステムの組み合わせが役立つと思うよ。例えば、テストやサンドボックス用に特注のIO互換モナドを使っても、下にあるエフェクトハンドラは、あなたのIOライクなモナドを呼び出すことができる。

アルジェブラ的エフェクトは区切られた継続の領域にあって、プログラムスタックで動作するんだ。モナドのトリックでは、コールスタックの5レベル上のエフェクトハンドラにすぐにジャンプして、そのスタックフレーム内のローカル変数を更新して、また5レベル下で実行を再開することはできないよ。

モナドと効果は、計算コンテキストについて考えるための補完的なアプローチとして見るのが一番いいと思うな、ライバルとしてではなくて。例えば、https://goto.ucsd.edu/~nvazou/koka/padl16.pdf を見てみて。

http://abstractionlogic.com

ApplicativeErrorやMonadError[0]型クラスを使うのと、これが実質的にどう違うの?もし単一の効果に限定するなら、あまり違いはないかもしれないけど、複数の効果を同時に持つと、明示的なサポートがあった方がネストされたモナドよりも扱いやすくなるよね(モナドの順序を選ぶ必要があったり、呼び出し関数の出力が使われるモナドのセットや順序と一致しない場合に再配置する必要があるから)。

HaskellのMonadErrorについては、かなり似てるよ。ただ、mtlスタイルにはいくつか問題があって、効果システムについては、著者が「mtlについては?」のところであんまり説明してないんだよね。https://hackage.haskell.org/package/effectful

アルジェブラ的エフェクトは、基本的に再開可能な例外だと思ってもらえればいいよ。つまり、Common Lispの条件みたいなもの?古いアイデアを名前を変えて繰り返すのが好きなんだ。

Smalltalkでは文字通り「再開可能な例外」だね。

Hacker Newsで議論の続きを見る