trace を入れて define、その定義された関数の適用される様子を追ってみます。
- 表示するための定義
instance Show LispVal where show = showVal2 showVal2 :: LispVal -> String showVal2 (String contents) = "String \"" ++ contents ++ "\"" showVal2 (Atom name) = "Atom \"" ++ name ++ "\"" showVal2 (Number contents) = "Number " ++ show contents showVal2 (Bool True) = "Bool True" showVal2 (Bool False) = "Bool False" showVal2 (List contents) = "List " ++ showValList contents showVal2 (DottedList head tail) = "DottedList " ++ showValList head ++ " " ++ showVal tail showVal2 (PrimitiveFunc _) = "PrimitiveFunc <primitive>" showVal2 (Func {params = args, vararg = varargs, body = body, closure = env}) = "Func{params=" ++ show args ++ " vararg=" ++ show varargs ++ " body=" ++ show body ++ " closure=<env>}" showValList :: [LispVal] -> String showValList xs = "[" ++ showValList' xs ++ "]" showValList' :: [LispVal] -> String showValList' [] = "" showValList' [x] = showVal2 x showValList' (x:xs) = showVal2 x ++ "," ++ showValList' xs
- define の様子を表示
eval env (List (Atom "define" : List (Atom var : params) : body)) = trace("eval env (List (Atom \"define\" : List (Atom "++show var++ " : "++ showValList params ++ " : " ++ showValList body++"))") (makeNormalFunc env params body >>= defineVar env var)
- define で定義された関数を実行する様子を表示
eval env (List (function : args)) = do func <- trace("eval env (function:("++showVal2 function++") args:"++ showValList args++")" ) (eval env function) argVals <- trace("-- func ("++showVal2 func++")<- eval env ("++showVal2 function++")") (mapM (eval env) args) trace("-- argVals ("++ showValList argVals ++")<- mapM (eval env) "++ showValList args) (apply func argVals)
- 関数を適用する様子
ここはもっと詳しく調べます。
apply :: LispVal -> [LispVal] -> IOThrowsError LispVal apply (PrimitiveFunc func) args = trace ("apply (PrimitiveFunc <primitive>) args:"++ showValList args) (liftThrows $ func args) apply (Func params varargs body closure) args = trace ("apply (Func "++showVal2 (Func params varargs body closure)++") args:"++ showValList args) (if trace ("num params:" ++show (num params)++" num args:"++show (num args)++" varargs == Nothing:"++ show (varargs == Nothing)) (num params /= num args && varargs == Nothing) -- NumArgs エラー "Expected 2 args; found values 1 20 30" then throwError $ NumArgs (num params) args else (liftIO $ bindVars closure $ -- zip:[("x",Number 10),("y",Number 20)] trace("zip:" ++ show (zip params args)) (zip params args)) >>= bindVarArgs varargs >>= evalBody) where remainingArgs = drop (length params) args num = toInteger . length evalBody env = liftM last $ mapM (eval env) body bindVarArgs arg env = case arg of Just argName -> liftIO $ bindVars env [(argName, List $ remainingArgs)] Nothing -> return env
それでは実行してみます。
- (define (add x y) (+ x y))
まず、(define (add x y) (+ x y))が評価されると "define" のひとつにマッチして、そこに定義されている関数が実行されます。
$ rlwrap runghc listing9.hs Lisp>>> (define (add x y) (+ x y)) eval env (List (Atom "define" : List (Atom "add" : [Atom "x",Atom "y"] : [List [Atom "+",Atom "x",Atom "y"]])) -- (define name value)のタイプ -- value を評価した結果が Env に var をキーとして保存される。 eval env (List [Atom "define", Atom var, form]) = eval env form >>= defineVar env var -- (define (add x y) (+ x y)) のタイプ -- 今回はこちら var:"add" params:[Atom "x",Atom "y"] -- body:[Atom "+",Atom "x",Atom "y"] eval env (List (Atom "define" : List (Atom var : params) : body)) = makeNormalFunc env params body >>= defineVar env var
makeNormalFunc が呼ばれ env params body の引数によりFunc型のデータが作られます。
Func型にはその都度今まで定義された環境(Env)も含まれています。
makeNormalFunc Func{params=["x","y"] vararg=Nothing body=[List [Atom "+",Atom "x",Atom "y"]] closure=<env>}
defineVar が呼ばれ、定義された関数名とそれを実行するための関数を作るのに必要なFunc型のデータが環境(Env)に保存されます。
Func型にはその都度今まで定義された環境も含まれています。
defineVar env var:"add" value:Func{ params=["x","y"] vararg=Nothing body=[List [Atom "+",Atom "x",Atom "y"]] closure=<env>} Func{params=["x","y"] vararg=Nothing body=[List [Atom "+",Atom "x",Atom "y"]] closure=<env>}
- (add 10 20)
eval env (function: args)のパターンにマッチ。
-- eval env (function:(Atom "add") args:[Number 10,Number 20]) eval env (List (function : args)) = do func <- eval env function -- "add" に対応する Func をEnvより取得 argVals <- mapM (eval env) args -- argsの要素を評価。 -- (今回はこれ以上評価できない) -- func を argVals に適用する。 apply func argVals
Lisp>>> (add 10 20) eval env (function:(Atom "add") args:[Number 10,Number 20]) -- func (Func{params=["x","y"] vararg=Nothing body=[List [Atom "+",Number 10,Number 20]] closure=<env>}) <- eval env (Atom "add") -- argVals ([Number 10,Number 20])<- mapM (eval env) [Number 10,Number 20] apply (Func Func{params=["x","y"] vararg=Nothing body=[List [Atom "+",Number 10,Number 20]] closure=<env>}) args:[Number 10,Number 20
次に apply 関数が呼ばれ、Func型から作られる関数を引数に適用します。
apply (Func Func{params=["x","y"] vararg=Nothing body=[List [Atom "+",Atom "x",Atom "y"]] closure=<env>}) args:[Number 10,Number 20]
引数部分が評価されます。
eval env (Atom "+")で "+" は本物の加算をする関数に。
[Atom "x",Atom "y"]を評価すると[Number 10,Number 20]になって、"+"から作られた関数が適用されて Number 30 になります。
eval env (function:(Atom "+") args:[Atom "x",Atom "y"]) -- func (PrimitiveFunc <primitive>)<- eval env (Atom "+") -- argVals ([Number 10,Number 20]) <- mapM (eval env) [Atom "x",Atom "y"] apply (PrimitiveFunc <primitive>) args:[Number 10,Number 20] Number 30
もっと詳しく追います。
この説明ではきっと初めての人には意味不明だ・・・。