型とクラス
型とは値の集合を指します。
型を明示的に宣言するには 「::」構文をつかいます。
-- 変数の型宣言 defaultLines :: Int defaultLines = 10 -- 関数の型宣言 myLength :: [a] -> Int myLength es = length es
aは型変数、[a]は任意の型のリストをあらわしている。このように型変数を含む型のことを多相型と言います。
代数的データ型
data宣言を使うことで代数的データ型を定義できます
データ宣言は次のように書きます。
data Anchor = A String String
"Anchor"が新しい型、"A"がその型の値を作る方法、二つのStringがその型の値がもつフィールドの型です。
"Anchor"が型コンストラクタ、"A"がデータコンストラクタといいます。
型コンストラクタ、データコンストラクタの名前は大文字で始まる必要があります。
データコンストラクタは関数とおなじように値に適用できます。しかし関数ではありません。
型コンストラクタとデータコンストラクタは名前空間が違うので同じ名前を使うことができます。
data宣言した型のフィールドにアクセスするには、データコンストラクタを使ったパターンマッチを利用します。
compiler (A url label) = ...
フィールドラベルを使うとラベルを使ってフィールドにアクセスできるようになります。
-- data宣言でのフィールドラベルをつかった例 data Anchor = A { aUrl :: String, aLabel :: String} -- パターンマッチでのフィールドラベルをつかった例 compile (A {aUrl = u, aLabel = l}) = ...
セレクタ
data宣言においてフィールドラベルを使用すると、そのフィールドラベルと同名の関数が自動的に宣言されます。
href :: Anchor href = A "Hoge" "Foo" main = do print (aLabel href)
データコンストラクタは関数と似ていますが、関数と違いそれ単体で値を構成できます。
なので
data OpenMode = ReadOnly | WriteOnly | ReadWrite
このように、OpenModeはReadOnlyもしくはWriteOnlyもしくはReadWriteである、というような書き方ができます。
また、異なったデータコンストラクタを|で区切って表現することができます。
data PTItem = Param Int | Text String
PTItemはInt型のフィールドをもつ、もしくはString型のフィールドをもつものであると表現しています。
また、data宣言は再帰的にも書けます。なので、たとえば、二分木を表現するとき、
data Tree = Empty | Fork Tree Tree
このように書くことができます。
type宣言
type宣言を使うと既存の型に別名をつけることができます。
type FilePath = String type MyList a = [a] type State s t = s -> (t, s)
型クラス
型クラスを使うと多相型に制約をつけられます。このような制約のついた多相性のことをアドホック多相と呼びます。
型クラスの例としてEqクラスをあげます。
Eqクラスは型に属する値のあいだで同値検査ができることを示します。つまり、(==)関数の引数にできるということです。
Eqクラスに対する(==)関数のように、その型クラスを特徴づける関数のことをクラスメソッドと言います。
Haskellでは型ごとにクラスメソッドの実装を変えられるようになっています。
これをクラスメソッドの多重定義といいます。
class宣言
新しい型クラスを宣言するにはclass宣言を使います。
class Eq a where (==), (/=) :: a -> a -> Bool x == y = not (x /= y) x /= y = not (x == y)
(==)と(/=)の定義がお互いを使って書かれていますが、こうすることで継承先でどちらか一方を定義すればよいことになります。
継承は次のように書きます
class (Eq a) => Ord a where compare ... (<), (<=), (>), (>=) ... max, min ...
instance宣言、deriving宣言
型をある型クラスのインスタンスであると宣言するにはinstance宣言を使います。
instance Eq Anchor where (A u l) == (A u' l') = (u == u') && (l == l')
上記のような実装が明らかな場合にはderiving宣言が使えます。
data Anchor = A String String deriving Eq