『Haskell入門』を読んでいて分からないことがあると、「やさしい Haskell 入門 (バージョン98)」を読むと良さそうです。
『Haskell入門』を読んでいて、「型は第一級の対象ではない」ということをもっと強調した方が分かり良いのでは、とHaskellの入門者として感じました。あと、『Haskell は、型の違う関数が共通の名前を共有するような C++ 流の多重定義 はサポートしていない』(やさしい Haskell入門より)ということもどこかに書いてあった方がいいのではと思いました(書いてあるのかも知れないけど)。ようするに、以下のような foo の定義は出来ないと例を示して書いてある方が良いのではないかと。
foo a b = 2 foo a = 1
こなれていない文章が時々ありました。例えば、データ構築子の説明。
Trueのように引数がない場合は、Trueそのものがなにがしかのデータになっています。一方 Just 1 のように引数を取ることで、なにがしかのデータを作ることになります。データを作るもの、ということでこういうものをデータ構築子(data constructor)と呼びます。
『Haskell入門』p101
とあります。この文章からだと、引数を取らないTrueはデータ構築子ではないと読んだ人は勘違いするかも知れません。というか、僕はそう解釈しちゃったんですが、『やさしい Haskell入門』によれば、True も無引数のデータ構築子なんだそうです。
データ構築子は第一級の対象であるということも、明示的に書いてあった方が良いのではと思いました。
これはOK。
data Point a = Pt a a foo = Pt
以下のような foo の定義は駄目。
data Point a = Pt a a foo = Point
と、文句ばかり書いているようですが、とても面白い本です。いや、ほんと。
しました。
Haskell の関数の引数のパターンに何が書けて、何が書けないか。パターンの構文。
以下のものの適当な組合せだけが許される。
ので、関数は(第一級の対象なんだけど)パターンとして許されない。
foo (\x -> 1) = 1
は駄目。
data Point a = Pt a a foo Pt = 1
も駄目。
自分の理解のためにユーザ定義のデータ型の説明を書いてみます。間違っているかも知れません。
以下のような構文によって、新たなユーザ定義型(この例では Point)とそのコンストラクタ(この例ではPt)を同時に定義することが出来ます。Pointを型構築子(type constructor)、Ptをデータ構築子(data constructor)と呼びます。
data Point = Pt Integer Integer
コンストラクタ Pt は Integer 型の引数を二つ受けとり、ユーザ定義型 Point を返す関数です。関数ですから、第一級の対象です。以下は簡単な例です。上でも述べたように、関数の引数のパターンには「データ構築子 k1 ... kn」という形は許されています。
data Point = Pt Integer Integer foo :: Point -> Integer foo (Pt a b) = a main = putStrLn (show result) where result = foo (Pt 1 2) -- -> 1
で、結局、Point は型なのか型構築子なのかどっちなんだ、という疑問を持たれるかも知れません。Point は(無引数の)型構築子であり、Pointという型を生成する、というのが正解のようです。無引数の型構築子があるなら、引数を取る型構築子もあります。以下のようなものです。
Ptが受けとる数として整数だけでなく、小数も受けとりたい場合に型変数を使います。
data Point a = Pt a a
これで、
data Point a = Pt a a foo :: Point a -> a foo (Pt a b) = a main = putStrLn (show result) where result = foo (Pt 1.2 2.4) -- -> 1.2
となります。さてここで注意すべきことは、 foo の引数の型は「Point a」であって、「Point」ではないということです。Point は型変数 a を受けとって、新たな型「Point a」を作ります。まさに型のコンストラクタ(type constructor)ですね。型構築子には当然 型変数だけでなく、定義済みの型を与えることも出来ます。
data Point a = Pt a a foo :: Point Integer -> Integer foo (Pt a b) = a main = putStrLn (show result) where result = foo (Pt 1 2) -- -> 1
ユーザ定義型は次のように定義することも出来ます。
data Bool = False | True
これは「Bool型は2つの値 TrueとFalseを持っている」と解釈することが出来ますが、「Bool型は True と False というふたつの(無引数の)コンストラクタを持っていて、コンストラクタはそれぞれ(返す型は同じだけど)別の値を返す。」と解釈することも出来ます。
再帰的に定義することも出来ます。
data Tree a = Leaf a | Branch (Tree a) (Tree a)
これは、「Tree a 型」はふたつのコンストラクタ Leaf と Branch を持っている。Leaf は「a 型」を引数としてひとつとり、Branch は「Tree a 型」の引数をふたつ取る。それぞれのコンストラクタは別の値を返す。 と解釈することが出来ます。
最近のコメント