Thinking Functionally with Haskell: 第2章再読 TypeとType Classメモ
Type関連で混乱してきたので、ちょっと一旦第2章に戻ってメモをまとめていく。
"In Haskell every well-formed expression has, by definition, a well-formed type. Each well-formed expression has, by definition, a value."
まず、式には型と値がある。Pythonのような言語では「値がまずあって、その値に型がある」と考えるのが自然だが、Haskellでは式の評価もまず「式の型を調べ、正しく型が定まっていれば次に値を評価する」という流れになる。だから上記の引用でも別途型と値についてこの順番で言及しているのだろう。
式の型
式の型は:type
で調べられる。
Prelude> :type 'A' 'A' :: Char Prelude> :type (head "ABC") (head "ABC") :: Char
後者は式が値に評価されずに型の情報を出しているのがわかる。
Prelude> :type 1 + 2 1 + 2 :: Num a => a Prelude> :type 1.0 + 2.3 1.0 + 2.3 :: Fractional a => a
数字を:type
で評価するとすぐに型クラスの概念が顔を出す。型クラスについては後述する。
関数の型
関数にも型がある。C言語などでもおなじみだが、引数と戻り値の型がそのまま関数の型になる。
Prelude> :type toUpper toUpper :: Char -> Char
toUpper
はChar
型をとってChar
型を返す。
Prelude> :type head head :: [a] -> a
head
は任意の型a
を含むリストをとり同じ型a
を返す。
Prelude> :type (+) (+) :: Num a => a -> a -> a
(+)
はNum
型クラスに属する型a
を二つとり、a
を返す。引数と戻り値のa
は3つとも同じ型。
型クラス
(+)
の引数・戻り値のような「特定の性質を持つ任意の型」を表したい時に型クラスを使う。
例えば==
で等価性を調べるような処理を含む関数の場合、==
が定義されているどのような型でも引数に使うことができる、逆に==
が定義されていない型では使えない、と関数の型定義で指定したい。その場合「Eq
型クラスの任意の型a」と表現する。
例えば:
isIn :: (Eq a) => a -> [a] -> Bool isIn _ [] = False isIn a (x:xs) | a == x = True | otherwise = isIn a xs
型の宣言
一番簡単な「新しい型の作り方」は他の型を使って宣言することだろう。
type Uuid = Int type Name = [Char] getName :: Uuid -> Name
Int
とChar
のリストとをUuid
とName
型とAliasして使っている。
他の型を使わず1から型を宣言する場合はdata declaration
を書く:
data Bool = False | True
False
とTrue
はdata constructor
。この概念はイマイチよくわかっていない。
Eq型クラスに属する型を宣言する場合:
data Color = Red | Green | Blue deriving (Eq)
あるいは
instance Eq Color where x == y = (somef x == somef y)
前者は==
は単にどのコンストラクタで作成されたかのみのチェック。後者は自分で==
の挙動が定義できる。
HaskellではTypeがType ClassのInstance。Python、C++、Javaのように「ObjectがClass/TypeのInstance」という用語の使い方と混同すると危ない。
型クラスの宣言
簡単なEq
型クラスの宣言は以下のようになる:
class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y)
ある型a
がEq
型クラスのInstanceであるためには、(==)
と(/=)
を正しい型で定義されている必要がある。ただし、(/=)
は(==)
を使ったデフォルト定義が存在するので、実際には(==)
を定義するだけでいい。
等価性だけではなく、大きさの比較も可能なOrd
型クラスの宣言:
class (Eq a) => Ord a where (<), (<=), (>=), (>) :: a -> a -> Bool
Ord
はEq
のsubclassである、と一行目で宣言し、Eq
が必要とする関数以外でOrd
に必要な関数を二行目で宣言している。