Haskell を使ってみる 5 (型)

前回の続き

Haskell を使ってみる 4 (タプル) - kntmr-blog

型宣言

式の型は :t コマンドで調べることができる。:: は xxx の型を持つことを意味する。

Prelude> :t 'a' -- Char 型
'a' :: Char
Prelude> :t "Hello" -- Char 型のリスト
"Hello" :: [Char]
Prelude> :t ('a',True) -- (Char 型, Bool 型) を持つ (タプルは個々の要素が型を持つ)
('a',True) :: (Char, Bool)

関数に明示的な型宣言を与えることができる。

removeLowercase :: [Char] -> [Char] -- 引数に1つの文字列を取って別の文字列を返すことを意味する

型変数

型変数を使うことで、型安全を保ったまま関数を複数の型に対して動作させることができる。

Prelude> :t head
head :: [a] -> a -- a が型変数で、任意の型を引数に取ることを表す

型変数を使った関数は多相的関数と呼ばれる。head 関数の場合、任意の型のリストを引数に取り、その型の要素を1つ返すことを表す。

型クラス

型クラスは何らかの振る舞いを定義するインタフェース。ある型クラスのインスタンスである型は、型クラスが持つ振る舞いを実装する。型クラスは関数の集まりで、ある型クラスに属する関数をその型クラスのメソッドと呼ぶ。

Prelude> :t (==)
(==) :: Eq a => a -> a -> Bool

=>型クラス制約と呼ばれる。等値性関数の場合、同じ型の引数を2つ取って Bool 型を返すことを表す。また、引数の型は Eq クラスのインスタンスであることを意味する。

等値性演算子 (==) は、実際には関数である。関数名が特殊文字から構成される場合、デフォルトで中置関数となる。その型を調べる場合や他の関数に渡す場合、前置関数として呼び出す場合は、丸括弧で囲う。

Eq 型クラス

等値性をテストできる型に使われる。Eq のインスタンス==/= を実装する。Haskell のすべての標準型は Eq のインスタンスである。

関数の型変数に Eq クラスの制約が付いている場合、その関数の定義のどこかで ==/= が使われていることを意味する。

Ord 型クラス

何らかの順序を付けられる型のための型クラス。比較演算子 (>) の場合、引数を2つ取り、それらが関係を満たすかどうかを表す Bool を返す。compare 関数の場合、Ord のインスタンスの型の引数を2つ取って Ordering を返す。Ordering は GT, LT, EQ のいずれかの値を取る型。

Prelude> :t (>)
(>) :: Ord a => a -> a -> Bool
Prelude> :t compare
compare :: Ord a => a -> a -> Ordering

Prelude> "abc" < "xyz"
True
Prelude> "abc" `compare` "xyz"
LT
Show 型クラス

ある値の型が Show 型のインスタンスであれば、文字列として表現することができる。show 関数は、ある型のインスタンスを引数に1つ取って文字列を返す。

Prelude> :t show
show :: Show a => a -> String

Prelude> show 1
"1"
Prelude> show 3.14
"3.14"
Prelude> show True
"True"
Read 型クラス

文字列を受け取り、任意の型のインスタンスの値を返すための型クラス。read 関数は、文字列を受け取り、Read のインスタンスの型の値を返す。

Prelude> :t read
read :: Read a => String -> a

Prelude> read "1" + 2
3
Prelude> read "[1,2,3,4]" ++ [5]
[1,2,3,4,5]

read 関数が返す値の型は Read のインスタンスであるが、具体的にどの型を返すかは返す値の使い方次第で判定する。式が取るべき型を明示する場合には型注釈 (::) を使う。

Prelude> read "1" -- これはエラー (返り値の型を推論できない)
*** Exception: Prelude.read: no parse
Prelude> read "1" :: Int -- 型注釈で型を指定する
1
Enum 型クラス

要素の値を列挙するための型クラス。Enum 型クラスのインスタンスの値はレンジの中で使うことができる。Enumインスタンスの型には、後者関数 succ や前者関数 pred が定義されている。

Prelude> succ '1'
'2'
Prelude> pred True
False
Bounded 型クラス

上限と下限を持つインスタンスを表すための型クラス。minBound 関数と maxBound 関数で調べることができる。

Prelude> :t minBound
minBound :: Bounded a => a -- 多相定数 (定数だが任意の型として振る舞うことができる)

Prelude> minBound :: Int
-9223372036854775808
Prelude> maxBound :: Char
'\1114111'

タプルを構成する要素がすべて Bounded のインスタンスであれば、そのタプル自身も Bounded になる。

Prelude> maxBound :: (Bool, Int, Char)
(True,9223372036854775807,'\1114111')
Num 型クラス

数を表すための型クラス。あらゆる数は多相定数として表されており、Num 型クラスの任意のインスタンスとして振る舞うことができる。

Prelude> 1 :: Int
1
Prelude> 1 :: Integer
1
Prelude> 1 :: Float
1.0
Prelude> 1 :: Double
1.0

* 演算子は、2つの数を引数に取って1つの数を返す。3つの数はすべて同じ型であることを意味する。

Prelude> :t (*)
(*) :: Num a => a -> a -> a

同時に複数の型のインスタンスとして振る舞うことはできない。

Prelude> (5 :: Int) * (10 :: Integer) -- これは型エラーとなる

<interactive>:109:15: error:
    • Couldn't match expected type ‘Int’ with actual type ‘Integer’
    • In the second argument of ‘(*)’, namely ‘(10 :: Integer)’
      In the expression: (5 :: Int) * (10 :: Integer)
      In an equation for ‘it’: it = (5 :: Int) * (10 :: Integer)
Floating 型クラス

浮動小数点数を表すための型クラス。この型クラスには Float と Double が含まれる。

Integral 型クラス

整数を表すための型クラス。この型クラスには Int と Integer が含まれる。Num は実数を含むすべての数を表す。fromIntegral 関数は何らかの整数を引数に取ってより一般的な数 (Num) を返す。整数と浮動小数点数を一緒に扱うような場合に使う。

Prelude> :t fromIntegral
fromIntegral :: (Num b, Integral a) => a -> b

Prelude> fromIntegral (1 :: Int) + 2.1
3.1
Prelude> (1 :: Int) + 2.0 -- これはエラー (Int と浮動小数点数を足し合わせようとしているため)

<interactive>:123:14: error:
    • No instance for (Fractional Int) arising from the literal ‘2.0’
    • In the second argument of ‘(+)’, namely ‘2.0’
      In the expression: (1 :: Int) + 2.0
      In an equation for ‘it’: it = (1 :: Int) + 2.0

今回は型について。