読者です 読者をやめる 読者になる 読者になる

e-mon

備忘録

HaskellでSchemeを書く#1

HakellでSchemeを書く

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

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

構文解析

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

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インスタンスとして宣言されているのは、比較とかを行うため。

というのも、以下のように、==とか、>とかは、EqOrdインスタンスの引数を受け取る関数として定義されているから。引数と引数の間に置ける関数を 中置関数というらしく、==+などが該当する。これらは、通常の関数呼び出しのように引数の前におけて、(==) 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関数?に 受け渡しをして、完了

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