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は一回で動くべきか?(続く)