裏書きに残りっぱなしになってたのを書いておく。GHCI上で pi
と打つと、πの値が表示できる。
Prelude> pi 3.141592653589793
Pythonだとそうはいかない。
>>> pi Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'pi' is not defined >>> from math import pi >>> pi 3.141592653589793
つまり、Haskellではグローバルな名前空間に pi
が出ているように見えるということ。これはなかなか気持ち悪い。
pi
の正体を見てみよう。
Prelude> :i pi class Fractional a => Floating a where pi :: a ... -- Defined in ‘GHC.Float’
Floating
型クラスに定義されているようだ。 Floating
型クラスは Prelude
モジュールに含まれているので、 import
などしなくても名前にアクセスできる。なるほど。
さて、拙著 Haskell入門 にも書いたが、この型クラスは浮動小数点数を意味する。この型クラスには pi
以外に、一般的な数値計算で必要な exp
や log
、 sin
などの関数も入っている。代表的なインスタンスは Double
と Float
。
Prelude> :i Floating class Fractional a => Floating a where pi :: a exp :: a -> a log :: a -> a sqrt :: a -> a (**) :: a -> a -> a logBase :: a -> a -> a sin :: a -> a cos :: a -> a tan :: a -> a asin :: a -> a acos :: a -> a atan :: a -> a sinh :: a -> a cosh :: a -> a tanh :: a -> a asinh :: a -> a acosh :: a -> a atanh :: a -> a GHC.Float.log1p :: a -> a GHC.Float.expm1 :: a -> a GHC.Float.log1pexp :: a -> a GHC.Float.log1mexp :: a -> a {-# MINIMAL pi, exp, log, sin, cos, asin, acos, atan, sinh, cosh, asinh, acosh, atanh #-} -- Defined in ‘GHC.Float’ instance Floating Float -- Defined in ‘GHC.Float’ instance Floating Double -- Defined in ‘GHC.Float’
pi
が定数ではなく型クラスのメソッドになっているのは、恐らく Float
と Double
で多相的に扱いたいからであろう。 GHCの実際の定義 は、以下。お、おう、といった感想。浮動小数点数だから、リテラルに小数点を何桁まで書いたかは精度とは関係ないってことだろう。結局は親の型クラスが持つ fromRational
を使って処理される。
instance Floating Float where pi = 3.141592653589793238 instance Floating Double where pi = 3.141592653589793238
一応、ghci上で違いを見ておく。
Prelude> pi :: Double 3.141592653589793 Prelude> pi :: Float 3.1415927
もう一つ疑問が残るのは、数ある数値系の型クラスの中で、なぜ Floating
というより具体性の高い型クラスに pi
が定義されたのか。 Floating
に定義されている関数は、実数値上に定義される関数である。Haskellでは、有理数は Fractional
型クラスで表現されている。しかし、当然ながら実数値を正確に表現できる型はない。そこで、コンピュータが一般的に用いる浮動小数点数にこれらの関数を定義した、ということだろう。
最後にまとめると、 pi
については以下となる。
Prelude
に定義されているので、識別子pi
でアクセスできる- メソッドになっているため、
Float
とDouble
で多相的に扱える - 実数という型がないので、浮動小数点数上の演算としているのだろう