bonotakeの日記

元・ソフトウェア工学系研究者、今・AI系エンジニア

さらに続き

※ 今しがた、親と付き合って酒をしこたま飲んだので、正しいことかいてるかは(いつも以上に)保証できません。日本語がおかしいかもしれません。
以上(1, 2)を踏まえて考えてみると、以下のような、プリミティブな関数をもう一個追加すれば、とても見通しが良くなる。

wrap :: Lang -> Computer Lang
wrap l = Run $ \p ->
         case p of
             P l' -> (I l p l', l)
             I l' _ _ -> (I l p l', l)

wrap(ネーミングセンスへの突っ込みはご勘弁を…) は、与えられたプログラム p に対して、言語 l での解釈を付与する。これは run (付与された解釈を実行する)と対になっていて、同じ言語で wrap をして run をすると元に戻る ((wrap >>= run) は return と等しい)。言い換えれば、モナド上で、wrap は run に対する右単位射右逆射 (section、切断) になってる。
※ 上の表現がわかりづらければ、wrap は「言語 l でコード化する」あるいは「言語 l のプログラムに直す」、run が「プログラムを実行する」と読み替えても良い。(wrap じゃなくて encode って名前の方が良かったかも…)
この wrap と run の組み合わせで、 インタプリタコンパイラも、モナドの操作として、かなりシンプルに書きなおせる。

-- インタプリタ (シンプル版)
interpret2 :: Lang -> Lang -> Computer Lang
interpret2 m l = run l >> wrap m

-- コンパイラ (シンプル版)
compile2 :: Lang -> Lang -> Lang -> Computer Lang
compile2 m t s = run s >> wrap t >> wrap m

どちらも、入力プログラムを仮定して、それをいっぺん run した後に

  • インタプリタは、自分自身が書かれた言語で wrap したもの。
  • コンパイラは、ターゲット言語で wrap し、更に自分自身が書かれた言語で wrap したもの。

これは、sumiiくんが初めに言っていたインタプリタコンパイラ+実行系」、って言明の補強にもなってる。run を実行系と解釈すれば、run を後ろにくっつける = 後ろの wrap を一個削る なので。(追記:ただし厳密には、「run_M o C」と言うのはあまり正しくないかも。以下注釈。*1

で、いずれにせよ、あるいはインタプリタコンパイラを何重に重ねた場合でも、元の入力プログラムを動作させるには、wrap されている分だけ run させればよい。


…多分。

*1:インタプリタコンパイラ+実行系」と解釈したときに、コンパイラにくっつく実行系は、文脈から考えれば run m(コンパイラが書かれた言語の実行系)ではなく、run t(ターゲット言語の実行系)だと思う。なので、実際には「run_M o C」というより、run_M が C の中に組み込まれると同値、と考えた方がよさそう。具体的には、C = compile2 とすれば、wrap t の後ろに run t をはさむと、interpret2 (= I) と同じ動作になる。

注:bonotakeは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。