View on GitHub

masaxsuzu's GitHub Pages

プログラミング言語「Koka」で遊ぶ

Algebraic effects and Handlersを実装した言語であるKokaで遊んだ。

一般的なプログラミング言語では馴染みがあるイテレータと例外構文を実装した。

実行環境

effectとhandler

effect

一般的なインターフェースのような定義のような定義になっている。

public effect enumerable<t> {
    fun yield(item:t) : ()
}

public effect panic<t> {
    fun happen(s:string) : ()
}

例えば、yield関数は

について、

を受け取り

を返し

を発生させる関数だ。

> :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関数は、

について、

を受け取り

を返し、

を発生させる

:t examples/foreach
forall<a,b,c,e>. (action : () -> <examples/enumerable<a>|e> b, f : (a) -> e c) -> e b

actionexamples/enumerable<a>|eになっており、enumerable<a>でない効果の処理を上位のハンドラに移譲できるようになっている。

キーワードresumeによって、継続を再開できる。yield関数の戻り値の型は()なので、resume(())のように再開する。

try_catchハンドラでは、継続を再開しないので、resume(())を使用していない。

具体的な計算

を使用して、具体的な計算を行う。

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
}

ここで、関数maindoの型はそれぞれ以下のようになっている。

> :t do
forall<a>. () -> <examples/enumerable<string>,examples/panic<a>> ()

> :t main
() -> console ()

mainを実行すると、iterateにより列挙されたアイテムをハンドルしならが、panicの処理を実施している。

> :l examples.kk
...

> main()
1
2
3
found a panic: oops

1
2
3
found a panic: oops
4
5
6

References