静的型付けとeval

いわゆるweb系のものをHaskellで書いていて感じる窮屈さの理由はやはりevalが無いことに起因するんじゃないかと思うのでevalについて考えてみる。

命令型・動的型付け言語におけるeval

こういう言語で提供されているeval(の類)はかなり強力で、モジュールのインポート等を含めて基本的になんでもできる。とはいえ、こういう言語はもともと任意の場所でグローバルな変更をもたらすような文を実行できるので、evalの意味としてはソースコードレベルでの文字列置換とほぼ同じで特に混乱はなく、興味は専ら力を制限して安全性を確保する側にある。

関数型・静的型付け言語におけるeval

といってもHaskell以外知らないので、専らHaskellの話になる。

まず無難なものから考えてみよう。

eval ("1+2" :: String) :: Int

みたいなものはDSLを作れば良い。

実際に欲しいのは、任意の型の式が得られるようなevalであって、

eval "\x->x" :: Either Error (a -> a)
eval "putStrLn \"hoge\"" :: Either Error (IO ())

こういうことができると便利そうだ。とはいえこういうevalがどのような型を持つのかよくわからない。とりあえず近似的に、Data.Dynamicなんかを使って

eval :: String -> Either Error Dynamic

みたいなものでも数行増えるだけでそれほど問題はないかもしれない。

さて、それでは型安全性を損なわずにどこまで強力なevalを考えることができるだろうか?例えばtop-level宣言はどうか?

実行中のコードをS1,evalするコードをS2として、S1でS2の定義を使ってるようなものはだめだろう。でも、S2からS1の定義を使うのは問題ない気がするし(さっきの例はそれが必須)、S2で何かを定義して、さらにevalするコードS3からS2の定義を使うのは問題ない気がする。というのも、この場合S2やS3は単体では問題があるけれど、evalのコンテキストでは充分な情報があるように思えるからだ。それに系全体としてはS1,S1+S2,S1+S2+S3という三つの状態を移動しているだけで、それぞれの状態はvalidだ。