Programming in Haskell Chapter 8 で躓く

in

どうにかこうにか読み進めてきた「Programing in Haskell」Chapter8 8.4 Sequencingで躓いてます。文字列の1番目と3番目の文字でペアを作成するというサンプルなんですがうまくいきません。実行例は次の通りです。

> parse p "abcdef"
[(('a','c'),"def")]

本書で、示されている定義は(前段からの続きも含めて)次のようになっています。

type Parser a = String -> [(a,String)]

return :: a -> Parser a
return v = \inp -> [(v,inp)]

item :: Parser Char
item = \inp -> case inp of
               [] -> []
               (x:xs) -> [(x,xs)]

(>>=) :: Parser a -> (a -> Parser b) -> Parser b
p >>= f = \inp -> case parse p inp of
                 [] -> []
                 [(v,out)] -> parse (f v) out

parse :: Parser a -> String -> [(a,String)]
parse p inp = p inp

p :: Parser (Char,Char)
p = do x <- item
       item
       y <- item
       Main.return (x,y)

ここで、最後のpの定義で怒られます。

Couldn't match expected type `Char'
against inferred type `[(Char, String)]'
Expected type: [((Char, Char), String)]
Inferred type: [(([(Char, String)], [(Char, String)]), String]

pの型はParser (Char,Char)、つまりString -> [(Char,Char),String]なのに対して、xやyは[(Char,String)]なので、"Main.return (x,y)"はおかしいよって言われています。実行例から類推すると、

parse p "abcdef"
-- x < item  x = [('a',"bcdef")]
item "abcdef"
-- item                  [('b',"cdef")] 
item "bcdef"
 -- y < item  y = [('c',"def")]
item "cdef"
-- Main.return (x,y) ????
(Main.return ('a','c')) "def" 

という流れだと思うのですが、そもそもdo式で2回目以降のitemに適用する引数がどこからくるのかよくわかりません。この前段ででてくる(>>=)が関係しているような気がするのですが、その定義は、

(>>=) :: Parser a -> (a -> Parser b) -> Parser b
p >>= f = \inp -> case parse p inp of
                 [] -> []
                 [(v,out)] -> parse (f v) out

で、[(v,out)] -> parse (f v) outですからこの章までの定義ではMain.returnしか適用できない気がします。実際に単独で実行してみたら、

> parse (item Main.>>= Main.return) "abcdef"
[('a',"bcdef")]
> parse (item Main.>>= item) "abcdef"
    Couldn't match expected type `Char' against inferred type `[Char]'
      Expected type: Char
      Inferred type: String
    In the second argument of `(Main.>>=)', namely `item'
    In the first argument of `parse', namely `(item Main.>>= item)'

とエラーになり、itemは連続して適用できませんでした。do式のようにitemを連続して適用するための定義を

(>>=) :: Parser a -> Parser b -> Parser b
p >>= f = \inp -> case parse p inp of
                    [] -> []
                    [(v,out)] -> parse f out

と修正すれば、

>parse (item Main.>>= item Main.>>= item) "abcdef"
[('c',"def")]

と、なるのでdo式でitemを3回繰り返したのと同じになるかと思うのですが、結局でているエラーはp::Parser (Char,Char)と、Main.return (x,y)は型が違うよっていうエラーには関係なさそう。

なんとか、Main.return (x,y)の前にx/yがCharになるような"fst ((!!) x 0)"みたいなマジックをどこかに仕込まなければいけないんだと思うのですが....

Chapter8 になって、(>>=)とreturnがでてきたあたり、とうとうアレなんでしょうか?まだHaskellを勉強し始めて日が浅く、テキストはこの「Programming in Haskell」しか読んでいないのですが、Haskellを学ぶにあたってはアレが鬼門だということはチラチラ聞こえています。一日考えて頭がごちゃごちゃになったので整理するつもりでこのエントリを書いてみたのですが、さらに混乱してきたのでとりあえずここまで。そろそろ英語だけのテキストだと限界かなぁーという気も少ししてきました。この本はどうも大学のテキストらしく基本的な知識のある人を対象にしているみたいだし、僕の英語力ではなんか大切なこと読み落としている気がします。

添付サイズ
chap8.hs1.05 KB

この記事のトラックバックURL:

http://hippos-lab.com/blog/trackback/330

Comments

item の型は Parser Char なので、<- で Parser を引きはがすと、文字が返ります。
(x,y) は (Char,Char) であり、return することによって Parser (Char, Char) となります。
これが、パーサー p の型です。

Parser は、状態モナドの一種として実現されています。状態モナドでは、裏でこっそり状態が渡されて行きます。Parser で渡される状態は、入力(パースの対象であるデータ)です。

なぜ、状態をこっそり渡せるのか理解するためには、Parser の >>= の実装を理解する必要があります。以下をみると、少しは分かるかもしれません。

http://d.hatena.ne.jp/kazu-yamamoto/20080604/1212573964

山本さんコメントありがとうございます。

なるほど、最後にちょっと違うよってかいてありますね。コードを試しながら読んでいたので途中で躓いてその先にまで目を通していませんでした。

結局この章はまだ理解できず結局前の章に戻って何度も読み直しているのですがまだまだですね。たとえば、P77の最後にある例で一番目と三番目の文字でパースする実装が、

p = do x <- item
           item
           y <- item
           return (x,y)

で、結果が

>parse p "abcdef"
[(('a','c'),"def")]

になっているのですが、xにバインドされた値は[('a',"bcdef")]でyのバインド値は[('c',"def")]でなぜreturn(x,y)が[(('a','c'),"def")]になるのかまだ理解できません。xとyがそれぞれ'a','c'ってならわかるんですが...

まだ、勉強はじめて正味二ヶ月くらいですが道のりは長く遠いです。

山本と申します。

8章の最後に書いてあるように、この章の例題は、そのまま入力しても動きません。
Hutton さんのページから、動くコードを入手するといいでしょう。

最初の type が newtype か data でないと動きません。
また、return や >>= は、関数の本体の前にデータ構成子(入手できるコードでは P) を付ける必要があります。

その内、すべての部品が Parser という型で揃っていることがすばらしいことに気付くでしょう。
IO の場合も、do の中はすべて IO で揃っているように。