セカイノカタチ

世界のカタチを探求するブログ。関数型言語に興味があり、HaskellやScalaを勉強中。最近はカメラの話題も多め

「第2期 第2回 H本読書会 in 秋葉原」でIntegralが気になった

前回のH本読書会*1にて、Integralという型クラスの説明があったのですが(P33)、上手く解説できなかったので調べてみた。

とりあえず、型の定義を。

Prelude> :i Integral
class (Real a, Enum a) => Integral a where
  quot :: a -> a -> a
  rem :: a -> a -> a
  div :: a -> a -> a
  mod :: a -> a -> a
  quotRem :: a -> a -> (a, a)
  divMod :: a -> a -> (a, a)
  toInteger :: a -> Integer
    -- Defined in `GHC.Real'
instance Integral Integer -- Defined in `GHC.Real'
instance Integral Int -- Defined in `GHC.Real'

どうやら、RealとEnumの型制約が付いているようです。

Realは、実数ですね。Enumは順序を示す型クラス。つまり順序立てる事ができる実数を特化したものがIntegralという型になるということです。

instanceを見ると、IntegerとIntが挙げられています。どうやら、IntegerとIntという似た者同士をまとめて扱えるようです。

関数を見てみると、quotやdivやmodなど掛けたり割ったりするようです。そして、toIntegerがあるので、IntegerとIntは、Integralを通してIntegerに変換することが出来るみたいですね。

そして本に解説のあった、「fromIntegral」関数ですが、IntとIntegerを引数にとって、Num型(Numは、IntやIntegerも属していますが、その他あらゆる(?)数値型を汎化したもっと守備範囲の広い型)に変換する関数なようです。

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

Haskellには暗黙的な自動変換がないので、演算に必要な型は自分で揃えてあげる必要があります。

例えば(+)(プラス)演算の定義はこんな感じになっているようです。

class Num a where
  (+) :: a -> a -> a

一見、引数が型パラメータ(a)となっていますので、Numのインスタンスならどんな型でも合いそうですが、(a -> a)となっている以上、第一引数と第二引数は同じ型でないとエラーとなります。

(1::Double) + (1::Int)

つまり、この様な例では、引数が(a -> b)となるため、型が合わずエラーです。

(1::Double) + fromIntegral (1::Int)

そこで、fromIntegralの出番となり、明示的に型変換してあげることにより、コンパイルできるようになるわけです。

型って素晴らしいですね。

*1:「すごいHaskell楽しく学ぼう!」の読書会です。念のため