bonotakeの日記

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

インタプリタとコンパイラのモナド

これモナドで書いてみた。いや、なんとなく…

data Lang = S | T | M    deriving (Eq, Show)
data Program = P Lang | I Lang Program Lang    deriving Show
data Computer l = Run { runner :: Program -> (Program, l) }

instance Monad Computer where
    return l = Run $ \p -> (p, l)
    Run r >>= f = Run $ \p ->
                  let (p', l) = r p in runner (f l) p'

-- 結果表示用
showComp p r = show $ runner r $ p

-- 計算の実行
run :: Lang -> Computer Lang
run l = Run $ \p ->
        case p of  -- 手抜き
            P _ -> (p, l) 
            I _ p' l' -> (p', l')

-- インタプリタ
interpret :: Lang -> Lang -> Computer Lang
interpret m l = Run $ \p -> (I m p l, m)

-- コンパイラ
compile :: Lang -> Lang -> Lang -> Computer Lang
compile m t s = Run $ \p -> (I m (I t p s) t, m)
  • 言語LでプログラムPを解釈実行した結果を(P, L)で表す。
  • Program の うち、I m p l ってのは、言語lで書かれたプログラムpを言語mに翻訳するプログラムのこと。
    • つまり、( (I m p l) , m) = (p, l) 。
  • interpret m l は、言語mで書かれた、言語l向けのインタプリタ
  • compile m t s は、言語mで書かれた、言語sからtへ変換するコンパイラ
    • ※ interpret と compile の実装は、後のエントリーで訂正してますので注意。

外してたらごめんなさい。引数の順番が気持ち悪いのは、モナドにしようとした都合です。

で、インタプリタの実行結果。

*Main> showComp (P S) $ run S
"(P S,S)"
*Main> showComp (P S) $ interpret M S
"(I M (P S) S,M)"
*Main> showComp (P S) $ interpret M S >>= run
"(P S,S)"

コンパイラの。

*Main> showComp (P S) $ compile M T S
"(I M (I T (P S) S) T,M)"
*Main> showComp (P S) $ compile M T S >>= run
"(I T (P S) S,T)"
*Main> showComp (P S) $ compile M T S >>= run >>= run
"(P S,S)"

Tで書かれたインタプリタを、Mで書かれたインタプリタで実行して、Sで書かれたプログラムPを動かす。

*Main> showComp (P S) $ return S >>= interpret T >>= interpret M >>= run >>= run
"(P S,S)"

ん?でも、この例だったらrunは一回で動くべきか?(続く

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