参考URL
#ソース
https://github.com/0zawa/scheme_in_48_hours/blob/master/evaluation_1.hs
ListValの表示
ListValをprint関数などで表示(文字列化)可能にします。
List,DottedList以外は単純に値を表示しています。
showVal :: LispVal -> String
showVal (String contents) = "¥"" ++ contents ++ "¥""
showVal (Atom name) = name
showVal (Number contents) = show contents
showVal (Bool True) = "#t"
showVal (Bool False) = "#f"
List,DottedListはLispValのリストをunwordsList関数に
渡してから連結しています。
showVal (List contents) = "(" ++ unwordsList contents ++ ")"
showVal (DottedList head tail) = "(" ++ unwordsList head ++ " . " ++ showVal tail ++ ")"
unwordsList関数はLispValのリストをStringに変換する関数です。
unwordsList :: [LispVal] -> String
unwordsList = unwords . map showVal
リストの個々の要素にshowValを適用し、
それをunwords関数に適用しています。
unwords関数はStringのリストを連結してStringにする関数です。
> unwords ["a","b","c"]
"a b c"
> unwords $ map show [1,2,3]
"1 2 3"
続いてLispValをShowクラスのインスタンスにします。
instance Show LispVal where show = showVal
これでshow関数の引数にLispValが渡された場合、
文字列が出力されるようになります。
文字列が表示できるようになったので、readExpr関数を修正します。
readExpr input = case parse parseExpr "lisp" input of
Left err -> "No match: " ++ show err
Right val -> "Found " ++ show val
"Found "のあとに文字列化されたLispValが
表示されるようになりました。
評価
続いて、LispValを評価するための関数を作ります。
まずは簡単なものから定義します。
eval :: LispVal -> LispVal
eval val@(String _) = val
eval val@(Number _) = val
eval val@(Bool _) = val
eval (List [Atom "quote", val]) = val
「@」を使っている箇所ははasパターンと呼ばれ、
@の前の変数には()の中全体が入ります。
evalを適用できるようにするために、
readExprの戻り値の型をStringからLispValに変更します。
readExpr :: String -> LispVal
readExpr input = case parse parseExpr "lisp" input of
Left err -> String $ "No match: " ++ show err
Right val -> val
この関数で変換されたLispValをevalに渡します。
この操作はmain関数で行います。
main :: IO ()
main = getArgs >>= print . eval . readExpr . head
「>>=」はバインドと呼ばれるものですが、少々表記がわかりにくいので
別の形式で書き直すと
main :: IO ()
main = do
x <- getArgs
(print . eval . readExpr . head) x
となります。
getArgsから取得したxはStringではなく[String]なので、
リストの先頭を取得するためheadを適用します。
次にその文字列にreadExprを適用してLispValを得ます。
更にそのLispValにevalを適用して評価結果のLispValを得ます。
これにprint関数を適用すると先ほど作成したshowVal関数が呼ばれて
文字列が出力されます。