型構築子

型構築子type constructorとはなんぞや。


と思って、このページの説明を書いていたけれど、
どうもここで言及しているものはデータ構築子(data constructor)らしい。
Boolなどが型構築子、Trueなどがデータ構築子。
第一部を書き終わるまで気が付かなかったので、第一部では全て間違って使ってる。
このwikiは学習中のメモなので、記録を残すためにあえて直さないから注意してね。



ここの「2.2 ユーザ定義の型」を参考に、実際に試してみる。

真偽値は型構築子らしい。真偽値というのは、

*Test> 1 == 1
True
*Test> 1 == 0
False

この返り値だね。じゃあ、新しいファイルを作ろう。

module Test2 where

p1 = True
p2 = False

これを読み込んで、値と型を見てみる。

*Test2> p1
True
*Test2> p2
False
*Test2> :t p1
p1 :: Bool
*Test2> :t p2
p2 :: Bool

どうも、型構築子というのはそれ自体の集合で型を決めるらしいね。

*Test2> :t True
True :: Bool

情報で見てみよう。

*Test2> :i Bool
data Bool = False | True
(略とか書くの面倒くさいから、適当に略)

|というのは普通、orを意味する。つまり、BoolはFalseかTrueのいずれか、ってこと。
で、略したけど、Boolは色々なクラスのインスタンスだと。
それがわかるとなにがうれしいかは、スーパークラス(続き)を読めばわかるかなあ。

それで、これは数値の型とは明らかに違うよね。Intと比べてみよう。

*Test2> :i Int
data Int = I# GHC.Prim.Int#

どうも、処理系のレベルで決められているみたい。
一応、数値を書いて、型を明示するとIntになるから、それも型構築子の一つなんだろうか。
まあ、真似して宣言を適当に書いてみよう。
ファイル名はモジュールと同じにすると分かりやすいよ(モジュール名が分かりにくいけど)。

module Test2b where

data MyType = Faux | Vrai | Hohe Int | Fuhe Float

p1 = Faux
p2 = Vrai
p3 = Hohe 3
p4 = Fuhe 1.5
p5 = Hohe

かなり適当な感じだけど、問題なく読み込める。

Ok, modules loaded: Test2b.

上手く行ってるか確認しようとしたら問題発生。

*Test2b> p1

Top level:
    No instance for (Show MyType)
      arising from use of `[[print]]' at Top level
    Probable fix: add an instance declaration for (Show MyType)
    In a 'do' expression: print it

前に出たのと同じ感じだ。どうも、このエラーメッセージは的を射ているようだ。
他に宣言が必要なんだね。まあ、それは後で調べることにしよう。
今回はもう一つの調査方法を使おう。

*Test2b> :t p1
p1 :: MyType
*Test2b> :t p2
p2 :: MyType
*Test2b> :t p3
p3 :: MyType
*Test2b> :t p4
p4 :: MyType
*Test2b> :t p5
p5 :: Int -> MyType

p1からp4まで問題なし。型の定義はこんな感じで出来るみたいだ。
p5はわざと中途半端な定義にしたけど、GHCiを怒らせることは出来なかった。

*Test2b> :t p5 7
p5 7 :: MyType

Intを与えると完成する部品になるんだね。

*Test2b> :t Fuhe 2
Fuhe 2 :: MyType

これでも種類が出るね。

さて、型構築子がどんなものか、ここまでで分かったことをまとめよう。

  • 型構築子は型のインスタンスを作れる。
  • 値を持つこともできる。値の取り方は、一つの型の中でも異なっていて良い。
  • 新しい型構築子から作られたインスタンスは、対応する関数を持たない(これも作らないと利用できない)。

関数は、代入できる型の種類を限定するんだよね。
でも、型の中にも型構築子があって、それぞれの形が違う。
だから、関数は型構築子ごとに別々の処理をしなくちゃいけない。それがパターンマッチングなんだ。

型と関数の関係にもう一つ考察を加えよう。新しい型は、扱える関数を作らないと意味がない。
逆に、新しい型だけを扱える関数は、既存の型の関数と混じることがない。
だから、別々の型を使うと決めてしまえば、それぞれの作用を取り違えないで済むね。
多分、それを利用して副作用を分離しているのだろう。

次は、色々な型パターンマッチング副作用のどれかに進んでね。

タグ:

+ タグ編集
  • タグ:
最終更新:2007年09月30日 11:35