言語ゲーム

とあるエンジニアが嘘ばかり書く日記

Twitter: @propella

Haskell でスタック言語を作る

Arrow はスタック言語だと口で言うだけじゃ信用してもらえないと思うので軽く作ってみる。

もしかして、Haskell で Joy (風言語)というのはとても難しいのではと思っていた。何故難しいかというと、スタックというのはリストなのだけど、リストというのは普通一つの型しか入れる事が出来ないのだ。でもリストじゃなくてタプルだったら問題ないと気が付いたのでやってみた。どちらかと言うと Cat 言語にあわせて作りました。サボってパーサーを作ってないので、文法は Arrow のままです。こんな風に実行します。リストの最後に factorial の Cat 風定義も置いておきました。

Cat> runCat $ push 3 >>> push 4 >>> add
(7,())
module Cat where
import Control.Arrow

data Quoation a = Q a
instance Show (Quoation a) where
    showsPrec _ (Q a) = showString "_q_"

push x st = (x, st)

-- Cat primitives Level0

inc (x, xs) = (x + 1, xs)
pop :: (a, b) -> b
pop (x, st) = st
dup (x, st) = (x, (x, st))
dip (q, (x, st)) = (x, (apply (q, st)))
swap (x1, (x2, st)) = (x2, (x1, st))
apply (Q q, st) = q st

-- if
branch (f, (t, (True, st))) = apply (t, st)
branch (f, (t, (False, st))) = apply (f, st)

-- Cat primitives Level1
dec (x, xs) = (x - 1, xs)

-- Cat primitives Level3
mul (x1, (x2, xs)) = (x1 * x2, xs)
add (x1, (x2, xs)) = (x1 + x2, xs)
lt (x1, (x2, xs)) = (x1 > x2, xs)
              
-- Useful functions
pushQ q st = push (Q q) st

runCat f = f ()

-- define rec_fac { dup 1 < [pop 1] [dup dec rec_fac *] if }
-- DEFINE fact == dup 1 < [pop 1] [dup pred fact *] branch.

fact :: (Integer, a) -> (Integer, a)
fact = dup >>> push 1 >>> lt >>> pushQ (pop >>> push 1) >>> pushQ(dup >>> dec >>> fact >>> mul) >>> branch
-- Main> runCat $ push 10 >>> fact
-- (3628800,())