プログラミング言語「Koka」で遊ぶ
Algebraic effects and Handlers
を実装した言語であるKokaで遊んだ。
一般的なプログラミング言語では馴染みがあるイテレータと例外構文を実装した。
実行環境
- ソースコードをDockerコンテナ上でビルドした。
effectとhandler
-
effect
-
handler
effect
一般的なインターフェースのような定義のような定義になっている。
public effect enumerable<t> {
fun yield(item:t) : ()
}
public effect panic<t> {
fun happen(s:string) : ()
}
例えば、yield
関数は
- 型
a
について、
a
の値item
を受け取り
()
を返し
- 効果
enumerable<a>
を発生させる関数だ。
> :t yield
forall<a>. (item : a) -> (examples/enumerable<a>) ()
handler
発生した効果をどのように処理するのかを定義する。効果ごとにハンドラを記述する。
public fun foreach(action, f) {
handle(action) {
yield(x) -> { f(x); resume(()) }
}
}
public fun try_catch(action, f) {
handle(action) {
happen(e) -> f(e)
}
}
public fun try_continue(action, f) {
handle(action) {
happen(e) -> { f(e); resume(()) }
}
}
例えば、foreach
関数は、
- 型
a,b,c
- 効果
e
について、
- ()型の値を受け取り、
b
を返し、効果examples/enumerable<a>
あるいは効果e
を発生させる関数action
a
型の値を受け取り、c
を返し、効果e
を発生させる関数f
を受け取り
b
型の値
を返し、
- 効果
e
を発生させる
:t examples/foreach
forall<a,b,c,e>. (action : () -> <examples/enumerable<a>|e> b, f : (a) -> e c) -> e b
action
がexamples/enumerable<a>|e
になっており、enumerable<a>
でない効果の処理を上位のハンドラに移譲できるようになっている。
キーワードresume
によって、継続を再開できる。yield
関数の戻り値の型は()
なので、resume(())
のように再開する。
try_catch
ハンドラでは、継続を再開しないので、resume(())
を使用していない。
具体的な計算
-
effect
enumerable
,panic
-
handler
foreach
,try_catch
,try_continue
を使用して、具体的な計算を行う。
public fun main() {
try_catch({
foreach({do()}, fun(s:string) {s.println})
}, found_panic)
breakLines()
foreach({
try_continue({do()}, found_panic)
}, fun(s:string) {s.println})
}
public fun do() {
iterate(["1", "2", "3"])
happen("oops")
iterate(["4", "5", "6"])
}
fun found_panic(s:string) {
val message = "found a panic: " + s
message.println
}
fun breakLines(){
"".println
}
ここで、関数main
とdo
の型はそれぞれ以下のようになっている。
> :t do
forall<a>. () -> <examples/enumerable<string>,examples/panic<a>> ()
> :t main
() -> console ()
main
を実行すると、iterateにより列挙されたアイテムをハンドルしならが、panic
の処理を実施している。
-
try_catch
の場合は、panic
のハンドル後に処理の再開はしない。 -
try_continue
の場合は、panic
のハンドル後に処理を再開している。
> :l examples.kk
...
> main()
1
2
3
found a panic: oops
1
2
3
found a panic: oops
4
5
6