Haskellのお勉強 その13
続、構造体スタイルとしての代数的データ型について
パターンマッチによるフィールドへのアクセス
データコンストラクタを使ったパターンマッチを利用することで、data宣言した型のフィールドにアクセスすることができる。
data Anchor = A String String compileAnchor (A url label) = …
上記は、compileAnchor関数の引数として、Anchor型の値をとって、その1つめのフィールドに
変数urlを束縛し、2つめのフィールドに変数lableを束縛している。
フィールドラベル
フィールドの数が多くなると、パターンマッチでフィールドにアクセスするのが大変になる。そこでフィールドラベルを用いることとなる。各フィールドに対してラベルをつけることで、
ピンポイントで代数的データ型のフィールドにアクセスすることが可能となる。
data Anchor = A { aUrl :: String, aLabel :: String } compileAnchor (A { aUrl = u, aLabel = l }) = ‥‥
パターンマッチにフィールドラベルを使うと、フィールドの順番を気にしなくてすむというメリットと、
必要がなければすべてのフィールドを書かなくてもよいというメリットがあり、
結果的に可読性が増すというメリットがある。
ちなみに、フィールドラベルを使って宣言した場合でも、
通常のパターンマッチによるフィールドへのアクセスを併用することができる。
セレクタ
フィールドラベルを使って代数的データ型を宣言すると、そのフィールドラベルと同名の関数が自動的に宣言されて、その関数を使ってフィールドの値を得ることができる。
このような関数のことを「セレクタ」と呼ぶ。そんだけ。
例えば、こんなの
data Anchor = A { aUrl :: String, aLabel :: String } href :: Anchor href = A "http://d.hatena.ne.jp/zecl/" "Bug Catharsis" main = do print (aLabel href)
"Bug Catharsis"と出力される。
フィールドの更新
Haskellには代入という概念がなく、変数は「値や式に便宜的につけた名前」ということだった。したがって、一度作った「フィールドを持った型」の値について、
その一部のフィールドの値だけを変更するようなことはできない。
しかし、一部のフィールドの値だけを変更したいときは、
次のようにすれば特定のフィールドだけを別の値に変更した値を作り出すことができる。
data Anchor = A { aUrl :: String, aLabel :: String } href :: Anchor href = A "http://d.hatena.ne.jp/zecl/" "Bug Catharsis" main = do print $ aUrl href print $ aLabel href print $ aUrl (href { aUrl = "http://www.yahoo.co.jp/" }) print $ aLabel (href { aLabel = "Yahoo!JAPAN" })
で、実行結果
"http://d.hatena.ne.jp/zecl/" "Bug Catharsis" "http://www.yahoo.co.jp/" "Yahoo!JAPAN"
もちろんこう書いたからといってhrefの値が変わるわけではなく、
hrefの値をもとにして一部のフィールドだけを別の値に変更した
新たな値を作り直しているにすぎないというところに注意。
多相的な型の宣言
data宣言を使うと、フィールドの型に型変数が含まれる代数的データ型も宣言することができる。型変数を使って宣言したフィールドは、特定の型に縛られない多相的なフィールドとなる。
たとえば、
data Stack a = MkStack [a]
これは、型変数aを使って多相的な代数的データ型「Stack a」を宣言している。
データコンストラクタはMkStackで、最初のフィールドの型が[a]である、と定義している
注意が必要なのは、「Stack a」全体で型を表しているということで、
「Stack」は「型コンストラクタ」であるという点。
これまでの例で宣言してきたAnchor型は、型変数を使っていなかったので、
たまたま型コンストラクタと型名が一致していたというわけ。
この「Stack a」型の値は、次のようなものを生成することができるよ。
data Stack a = MkStack [a] MkStack [True, False,True] -- Stack Bool型の値を作成 MkStack ['a', 'b', 'c'] -- Stack Char型の値を作成 MkStack ["hogehoge", "piyopiyo"] -- Stack String型の値を作成
型コンストラクタとデータコンストラクタ
型コンストラクタとデータコンストラクタは名前空間が分かれているので、両方に同じ名前を使うこともできる。
たとえば、
data Anchor = Anchor String String
と定義すれば、最初に出てきたAnchorは型コンストラクタのAnchorで、
その次に出てくるAnchorはデータコンストラクタのAnchorとなる。
こうすると、「型の名前(型コンストラクタ)」と
「その型の値を作りたいときに使う名前」が同じになるので、慣れると便利かもしれない。