HaskellでSchemeを書く#1
HakellでSchemeを書く
Haskellを覚えるのに、以下の48時間でSchemeを書こう、に挑戦することにした。 HaskellもSchemeも全く知らないところからのスタートでかなり無謀感漂う。
参考元:48時間でSchemeを書こう
構文解析
symbol :: Parser Char symbol = oneOf "!#$%&|*+-/:<=>?@^_~"
この1つ前の1.最初の一歩の章から考えるに、これは、symbol
という名の関数を Parser Char
という型として宣言、2行目の代入式(束縛する、というらしい)の右辺値で定義される動作の結果の値を返す関数となる、という理解。
関数の宣言は、
関数名 :: 型シグネチャ 関数名 引数 = 式の定義
となる。
そして、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
を2つ引数として受け取り、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
関数?に
受け渡しをして、完了
だいぶ寄り道してしまった感あるけど、全然理解出来てない感じがやばい。