すごいH本の覚え書き 第2章「型を信じろ」

Haskellの型についての章。

一般的なHaskellの型

  • Int

有界の整数値。範囲は環境依存。

  • Integer

有界でない整数値。大きい値を扱う場合はこっちを使う。動的にメモリを確保するため、Intの方が早い。

  • Float, Double

小数点型。他言語と同じ。

  • Bool

真偽値型。TrueとFalseの2値。

  • Char

Unicode文字を表す。シングルクォートで囲む。

  • タプル

タプルも型。ちなみにタプルの要素の最大値は62らしい。なんだその制約。

この時点では厄介な文字列周りの型について触れてないのがすごく良い構成だと思った。

型変数

他言語におけるジェネリクスみたいなやつ。汎用的な関数のシグネチャを見てみると使われている。

:t head
head :: [a] -> a

型変数を用いた関数を 多相的関数 というらしい。おそらく数学由来の用語なんだろう。

型クラス

他言語におけるインターフェースのようなもので、ある型をその型クラスのインスタンスとして定義することは、その型に特定の振る舞いを持たせることを意味する。例えば、Eq型クラスのインスタンスとなる型は「同一性を判定できるもの」と定義することができる。

  • Eq型クラス

等値性をテストできる型クラス。(==) と (/=) を定義していなければならない。

  • Ord型クラス

なんらかの順序を付けるための型クラス。

  • Show型クラス

これのインスタンスは文字列として表現することができる。例えば、以下のようにshow 関数を適用したときに文字列に変換できる。

show 3
#=> "3"
  • Read型クラス

Show型クラスの逆で、文字列から変換可能な値を表す。例えば、文字列にread関数を適用したときによしなに変換してくれる。

read "3" + 5
#=> 8

ただし、以下のように適用するとエラーになる。

read "4"

read関数のシグネチャは以下。

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

文字列を受け取り、任意のRead型クラスのインスタンスaを返す。先程の read "3" + 5の例では、GHCが変換する値を文脈から推測できた(めちゃくちゃ賢いね)。しかし、単体で適用するとコンテキストがないため、何の型に変換すれば良いかわからなくてエラーになる。この場合、 型注釈 を使い、明示的に変換先の型を指定する必要がある。

read "4" :: Int
  • Enum型クラス

順番に並んだ型、要素の値を列挙できる型。Ordとちょっと似ているが、比較できる必要はない。これのインスタンスは後者関数succ, 前者関数pred, さらにはrange記法を使ったりできる。比較の際に使用するLT, GTなどもこれのインスタンス。

[LT .. GT]
#=> [LT, EQ, GT]
  • Bounded型クラス

上限と下限を持つ値を表す。minBound, maxBoundを適用できる。

ghci> minBound ::Int
-9223372036854775808

ちなみにminBound(maxBound)のシグネチャは以下のようになっている。

ghci> :t minBound 
minBound :: Bounded a => a

引数を取らず、aが何の型なのかを指定する必要があるため型注釈を用いなければならない。さっき出てきたreadと同じ。 多相定数と呼ばれる。

  • Num型クラス

実数全体を表す型クラス。

ghci> :t 3
3 :: Num a => a

全ての数は多層定数になっている。そのため、Num型クラスの任意のインスタンスとして振る舞うことができる。

20::Int
5.5::Double
5.5::Float
  • Floating

DoubleとFloat。浮動小数点数のための型クラス。

  • Integral 整数を表す型クラス。fromIntegral関数がある。

数値計算について

数値計算周りはちょっと気をつけなきゃいけなさそう。一般的な演算子のシグネチャは以下のようになっている。

ghci> :t (+)
(+) :: Num a => a -> a -> a

つまり、Num同士ならよしなに推論してくれるが、IntとDoubleに型付けられた数字同士の計算はエラーになる。

ghci> (3 :: Int) + (4.5 :: Double)

この時のための便利な関数として fromIntegral が紹介されている。Integral(整数値)をNum型クラスの任意のインスタンスbに戻してくれるため、これで型推論を利かせられるようになる。

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

ghci> fromIntegral((3 :: Int)) + (4.5 :: Double)
7.5

感想

数値が Num a => a という定義になっているのは面白い。