エフェクト
Yulang の副作用は algebraic effect として扱う。effect は宣言され、呼び出され、handler で処理され、型に追跡される。
宣言
act console:
our read: () -> str
our println: str -> ()act は effect interface を作る。operation は our または pub の signature として並べる。同名の companion module が作られ、console::read のように参照できる。
呼び出し
say "hello"
my line = console::read()effect operation は普通の関数のように呼べる。呼び出した関数の型には、その effect が残る。
Handler
our run_console(action: [console] 'a): 'a = catch action:
console::read(), k -> run_console(k "42")
console::println _, k -> run_console(k ())catch expr: は handler である。operation arm は operation の引数と continuation k を受け取る。k value を呼ぶと、operation の場所へ値を返して計算を続ける。
handler は effect row から処理済み effect を取り除く。たとえば action が [console; e] 'a のような computation type を持つなら、run_console action は残りの effect だけを持ち、'a を返す。
handler は shallow
Yulang の handler は shallow である。arm は一度だけ operation を受け取り、 k で再開された計算は同じ handler に自動では包まれない。再開後に同じ effect を起こしても、handler は二度目に発火せず、effect はそのまま外側へ 伝播する。
operation を連続で受けたいときは、arm 側で continuation を再度 handler で 包む書き方をする。
our run_console(action: [console] 'a): 'a = catch action:
console::read(), k -> run_console (k "42") -- ← run_console で巻き直す
console::println _, k -> run_console (k ())
v -> vこのリファレンスの handler 例の多くがこの自己再帰形なのはそのため。 operation が 1 度しか起きない前提なら再帰を省ける。effect を繰り返し起こす 任意の計算を扱うときは再帰が必須。
Effect row
[console; e] str
() -> [console; e] str[...] の中に effect を並べる。; e は残りの effect を表す row variable である。[_] は annotation 内で推論に任せるための placeholder として使えるが、effect row type そのものの標準形ではない。
effect は型引数を持つこともできる。
act ref_update 'a:
our update: 'a -> neverそのため row には ref_update int のような entry も入る。型表示では Greek variable が出ることがあるが、source annotation では row tail に e のような名前を使うのが普通。
effect-row method は nominal value companion ではなく、receiver の effect row から選ばれる。
use std::undet::*
(each [1, 2, 3]).list同じ row 内の複数の effect が同名 method を持つ場合、row が絞られるまで selection は ambiguous になる。
Propagation
effectful な関数を呼ぶと、その effect は外側へ伝播する。handler を置いた場所でだけ取り除かれる。
our ask() = console::read()
our run_console(action: [console] 'a): 'a = catch action:
console::read(), k -> run_console(k "42")error 宣言
error は、enum、throwing operation を持つ act、impl Throw、wrap / up companion helper をまとめて生成する短縮構文である。
error fs_err:
not_found path
denied path
invalid_path path各 variant は data constructor と effect operation の両方として使われる。値として必要な文脈では fs_err の値になり、effect として必要な文脈では fs_err effect を発火する operation になる。
fail、名指し catch、wrap、from 集約、up の使い方を含む全体像は エラー を参照。
通常の enum variant でも from は使える。キャスト を参照。