HakellでSchemeを書く

Haskellを覚えるのに、以下の４８時間でSchemeを書こう、に挑戦することにした。 HaskellもSchemeも全く知らないところからのスタートでかなり無謀感漂う。

参考元：48時間でSchemeを書こう

構文解析では、PerSecライブラリでパーサーを書く。

symbol :: Parser Char symbol = oneOf "!#$%&|*+-/:<=>?@^_~"

この１つ前の1.最初の一歩の章から考えるに、これは、 symbol という名の関数を Parser Char という型として宣言、２行目の代入式（束縛する、というらしい）の右辺値で定義される動作の結果の値を返す関数となる、という理解。

関数の宣言は、

関数名 :: 型シグネチャ 関数名 引数 = 式の定義

となる。

そして、Parserとはモナドなるものらしい。進めていくうちに理解出来るハズ。

readExpr :: String -> String readExpr input = case parse symbol "lisp" input of Left err -> "No match: " ++ show err Right val -> "Found value"

で、 symbol というパーサで文字列をパースして結果を得るための関数が、これ。

readExpr という関数は、 String 型を引数で受けて、 String で返すんだよ、という宣言をしている。

これは複数の引数もこの形式で書くらしく、 textFunction :: String -> String -> String と書くと、 String を２つ引数として受け取り、Stringを返す関数として宣言されて、 function "abc" "def" みたいな書き方が出来るようになる。

カリー化というものらしく、要は、 引数->(引数->返り値) のようになっていて、一つ目の引数を受け取った後、 (引数->返り値) という型宣言を持つ関数を返す、という風に解釈されるらしい。

higher order functions - Learn Your Haskell for Grate Goodに、ものすごいわかりやすい例があった。

ghci > let multTwoWithNine = multThree 9 ghci > multTwoWithNine 2 3 54 ghci > let multWithEighteen = multTwoWithNine 2 ghci > multWithEighteen 10 180

で、だいぶ寄り道したけども、 readExpr input = case parse symbol "lisp" input of~~ という部分は、以下の構文で表されてる。これは単に、 input っていう String の引数を受け取り、 parse symbol "lisp" input という parse 関数を実行するということで、 parse 関数は多分、 parse :: Parser某型関数 -> String -> String -> Eitherなる型 という宣言になるのではないか、ということで確認してみた。

ghci 上では、 :m module名 という感じでモジュールのインポートが行えるらしい、ので、以下を実行。

Prelude > : m Text.ParserCombinators . Parsec Prelude Text.ParserCombinators . Parsec > : t parse parse :: Text.Parsec . Prim.Stream s Data.Functor . Identity.Identity t => Text.Parsec . Prim.Parsec s () a -> SourceName -> s -> Either ParseError a

type => a っていう書き方は、以降登場する a なる文字は、 type という型なんですよーという宣言ならしい、けど、丁度その部分が何書いてるかよくわからん。 多分、 Text.Parsec.Prim.Parsec s として定義される関数の値の返り値の型をaとして、 SourceName と、 s を引数に、 Either 型を返している気がする。宿題。

Eitherは、 data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show) のように宣言されていて、 deriving 修飾子によってある型クラスのインスタンスである、ということを指定するらしい。 Eq とか Ord のインスタンスとして宣言されているのは、比較とかを行うため。

というのも、以下のように、 == とか、 > とかは、 Eq や Ord インスタンスの引数を受け取る関数として定義されているから。引数と引数の間に置ける関数を 中置関数というらしく、 == や + などが該当する。これらは、通常の関数呼び出しのように引数の前におけて、 (==) 100 200 みたいなことが出来る。

また、中置関数も前置出来て、その際は 128 \ max` 256`のように書くことが出来る。(あんまりいい例じゃないけど。)

Prelude > : t ( == ) ( == ) :: Eq a => a -> a -> Bool Prelude > : t ( > ) ( > ) :: Ord a => a -> a -> Bool

Either は名前の通り、 Left or Right のどちらかが格納されているデータ型なんだな、という理解でとりあえず進む。

そんな感じで String を受けてパースして String を返す関数が実装されたので、 IOモナド なるものによって定義されている main 関数？に 受け渡しをして、完了

だいぶ寄り道してしまった感あるけど、全然理解出来てない感じがやばい。