ラムダ計算機 Ver 0.1
ラムダ計算機
ラムダ計算の β - 縮約を自動で計算するプログラムを作った。ラムダ記号の代わりに英小文字の l を使う。変数は一文字でも文字列でもよい。ラムダ抽象と関数適用の括弧は省略できない。関数適用は変数間にスペースが必要だ。= 記号を使って、ラムダ式を変数に束縛できる。などの特徴がある。
GitHub https://github.com/tnomura9/lambda-calculus
実際の使い方は次のようになる。
$ runghc lambda.hs lambda> a a lambda> (lx.x) (lx.x) lambda> ((lx.x) a) a lambda> ((lx.(x y)) a) (a y) lambda> (= true (lx.(ly.x))) lambda> ((true a) b) a lambda> :quit
ラムダ計算機 Ver 0.1
ラムダ計算機 Ver 0.1 ではテキストファイルから関数の定義を読み込めるように改良した。この改良でコマンドが :load と :quit の2つになった。:load コマンドでテキストファイルの (= name term) 形式の項を読み込んで関数の定義を登録できる。あとの使い方は前のプログラムと一緒だ。使用例は次のようになる。
$ runghc lambda.hs lambda> :load filename: defined.txt lambda> true (lx.(ly.x)) lambda> false (lx.(ly.y)) lambda> 1 (ls.(lz.(s z))) lambda> (succ 1) (ls.(lz.(s (s z)))) lambda> :quit
:load コマンドで取り込む定義ファイルは次のように記述する。
$ cat defined.txt (= true (lx.(ly.x))) (= false (lx.(ly.y))) (= NOT (lx.((x false) true)))) (= 0 (ls.(lz.z))) (= succ (lw.(ls.(lz.(s ((w s) z)))))) (= 1 (succ 0)) (= 2 (succ 1)) (= 3 (succ 2)) (= 4 (succ 3)) (= 5 (succ 4))
lambda.hs プログラムのソースは次のようになる。
ファイル名: lambda.hs
import Data.Maybe import Data.IORef import qualified Data.Map as Map import Text.Parsec import System.Console.Haskeline import Control.Monad.Trans import Control.Monad.Identity import qualified Control.Exception as Exc
type Name = String
data Term = Var Name
| Abs Name Term
| App Term Term
| Bind Name Term
deriving (Show)
type Env = Map.Map String Val
data Val = Atom String
| FunVal Env String Term
| Default Val Val
deriving (Show)
evalTerm :: Env -> Term -> Eval0 Val
evalTerm env (Var x) = case Map.lookup x env of
Just val -> return val
Nothing -> return (Atom x)
evalTerm env (Abs s t) = return $ FunVal env s t
evalTerm env (App t1 t2) = do
v1 <- evalTerm env t1
v2 <- evalTerm env t2
case v1 of
FunVal env' s body -> evalTerm (Map.insert
s v2 env') body
_ -> return $ Default v1 v2
type Eval0 a = Identity a
runEval0 :: Eval0 a -> a runEval0 ev = runIdentity ev
parseTerm :: String -> Either ParseError Term
parseTerm input = parse term "lambda" input
where
term = var <|> abstr <|> appl <|> bind
var = try (do
x <- many1 alphaNum <* spaces
return (Var x))
abstr = try (do
string "(l"
x <- many1 alphaNum <* string "."
e1 <- term
string ")" <* spaces
return (Abs x e1))
appl = try (do
string "(" <* spaces
e1 <- term
e2 <- term
string ")" <* spaces
return (App e1 e2))
bind = try (do
string "(=" <* spaces
x <- many1 alphaNum <* spaces
e1 <- term
string ")" <* spaces
return (Bind x e1))
unparse :: Val -> String
unparse (Atom x) = x
unparse (FunVal e x t) = "(l" ++ x ++ "." ++ (unparse (runEval0 (evalTerm
e t))) ++ ")"
unparse (Default v1 v2) = "(" ++ (unparse v1) ++ " " ++ (unparse v2) ++ ")"
loadFile :: IORef Env -> InputT IO ()
loadFile ioref = do
fname <- getInputLine "filename: "
case fname of
Just fnm -> lift $ bindAll ioref fnm
Nothing -> outputStrLn "error"
loadTerm :: String -> IO [Either ParseError Term] loadTerm fname = map parseTerm <$> lines <$> readFile fname
bindOneTerm :: IORef Env -> Either ParseError Term -> IO ()
bindOneTerm ioref tm = do
env <- readIORef ioref
case tm of
Right (Bind x e) -> do
val <- return $ runEval0 $ evalTerm env e
writeIORef ioref (Map.insert x val env)
Right exp -> do print exp; print "not written"
Left err -> print err
bindAll :: IORef Env -> String -> IO ()
bindAll ioref fname = do
Exc.catch ( do
binds <- loadTerm fname
mapM_ (bindOneTerm ioref) binds
return ()
) $ \(SomeException e) -> print e
main :: IO ()
main = do
global <- newIORef Map.empty
runInputT defaultSettings (loop global)
where
loop :: (IORef (Map.Map Name Val)) -> InputT IO ()
loop glb = do
minput <- getInputLine "lambda> "
case minput of
Nothing -> return ()
Just ":quit" -> return ()
Just ":load" -> do loadFile glb; loop glb
Just input -> do
env <- lift $ readIORef glb
expr <- return $ parseTerm input
case expr of
Right (Bind x e) -> do
let val = runEval0 $ evalTerm env e
lift $ writeIORef glb (Map.insert x val env)
Right exp ->
outputStrLn $ unparse $ runEval0 $ evalTerm env exp
Left err ->
outputStrLn $ show err
loop glb
ラムダ計算機 設定ファイル
ラムダ計算機に関数の定義を読み込ませる設定ファイル defined.txt。ラムダ計算機に設定ファイルを読み込ませるためには次のように :load コマンドを用いる。ラムダ計算機 lambda.hs は runghc で起動することもできるし、ghc lambda.hs でコンパイルすれば lambda コマンドで実行できる。$ runghc lambda.hs lambda> :load filename: defined.txt lambda> ((Y g) 3) (ls.(lz.(s (s (s (s (s (s z))))))))
設定ファイル: defined.txt
$ cat defined.txt (= true (lx.(ly.x))) (= false (lx.(ly.y))) (= NOT (lx.((x false) true)))) (= AND (lx.(ly.((x y) false))))) (= OR (lx.(ly.((x true) y))))) (= IMPLY (lx.(ly.((OR (NOT x)) y)))) (= 0 (ls.(lz.z))) (= succ (lw.(ls.(lz.(s ((w s) z)))))) (= pred (ln.(ls.(lz.(((n (lx.(ly.(y (x s))))) (lx.z)) (lx.x)))))) (= add (lx.(ly.((x succ) y)))) (= mult (lx.(ly.((x (y succ)) 0)))) (= 1 (succ 0)) (= 2 (succ 1)) (= 3 (succ 2)) (= 4 (succ 3)) (= 5 (succ 4)) (= 6 (succ 5)) (= 7 (succ 6)) (= 8 (succ 7)) (= 9 (succ 8)) (= isZero (lz.(((z false) NOT) false))) (= pair (lx.(ly.(ls.((s x) y))))) (= fst (lp.(p true))) (= snd (lp.(p false))) (= cons (la.(lb.(lf.((f a) b))))) (= car (lx.(x true))) (= cdr (lx.(x false))) (= Y (lf.((lx.(f (x x))) (lx.(f (x x)))))) (= g (lf.(ln.(((isZero n) 1) ((mult n) (f (pred n))))))) (= Z (lf.((lx.(f (ly.((x x) y)))) (lx.(f (ly.((x x) y))))))) (= I (lx.x)) (= K (lx.(ly.x))) (= S (lx.(ly.(lz.((x z) (y z))))))