effect handlerとnon-local exit
code:sk
class File
def self.open(path: String, block: Fn1<File, Void>)
let f = Internal.open_file(path)
block.call(f)
f.close
end
end
というのがあるとする。Shiikaには例外がないのでf.closeは必ず実行される…と思いきや、non-local exitを実装するとそうではなくなる
code:sk
class Config
def self.load(path: String) -> Result<Config>
File.open(path) do
return Error.new if ...
...
end
end
end
上記のようにblock内からreturnでloadを抜けたときでも、f.closeは実行されるようにしたい
code:sk
class File
def self.open(path: String, block: Fn1<File, Void>)
let f = Internal.open_file(path)
defer { f.close }
block.call(f)
end
end
#Go のdeferみたいな文法を入れるというのは一つの案 この場合実装としてはnon-local exitする際にdeferされた処理があるかを見て必要なら実行するという感じになる
で
ここでnon-local exitをeffectと考えてみる
code:sk
class Boundary<T> : Effect
def escape(value: T, k: Continuation) -> T
value
end
end
class Config
def self.load(path: String) -> Result<Config>
Boundary<Error>.new.handle {|$boundary: Boundary|
File.open(path) do
$boundary.escape(Error.new) if ...
...
end
end
end
end
class File
def self.open(path: String, block: Fn1<File, Void>, $outer_boundary: Boundary)
let f = Internal.open_file(path)
...ここうまく書けなかったけど、block.call内でBoundary.escapeが実行されたら
f.closeを行ってから、エフェクトを上げ直して外側のハンドラにキャッチさせる
end
end
みたいなことをすると、non-local exitをeffect systemの枠組みに乗せられるのかもしれない
boundaryという名前はScala3から取った。実装がどうなってるのかはよく知らない