Numbers in Haskell are typed of course. They also exist as instances of a numeric typeclass hierarchy. I was confused with converting and working with numbers of different types, and not being sure which functions were polymorphic and could work with different numeric types. So I created a little diagram. Do note that the typeclass hierarchy of Haskell actually does somewhat follow the hierarchy of numbers in Math.
The first step to realise is that all literal numbers in Haskell are constructors
that construct some instance of the Num
typeclass. There's a number of
typeclasses that inherit from the Num
typeclass, this means it's possible to
type hint that a particular literal is of a particular type, such as:
> 1 :: Fractional a => a --^ literal 1 some instance in Fractional
1.0
> 1 :: RealFloat a => a --^ literal 1 is some instance in RealFloat class
1.0
> 1 :: Integer --^ Integer is a type instance part of the Integral typeclass
> 1
In GHCI, you can find out information about each type class by typing:
> :info Float
class (Num a, Ord a) => Real a where
toRational :: a -> Rational
-- Defined in ‘GHC.Real’
instance Real Integer -- Defined in ‘GHC.Real’
instance Real Int -- Defined in ‘GHC.Real’
instance Real Float -- Defined in ‘GHC.Float’
instance Real Double -- Defined in ‘GHC.Float’
This actually shows you the typeclass that RealFloat is encapsulated in and its possible instances.
If you have a particular type instance, and you want to know what type classes
it is part of, you can also use the the :info
command:
> :info Integer
data Integer
= integer-gmp:GHC.Integer.Type.S# GHC.Prim.Int#
| integer-gmp:GHC.Integer.Type.J# GHC.Prim.Int# GHC.Prim.ByteArray#
-- Defined in ‘integer-gmp:GHC.Integer.Type’
instance Enum Integer -- Defined in ‘GHC.Enum’
instance Eq Integer -- Defined in ‘integer-gmp:GHC.Integer.Type’
instance Integral Integer -- Defined in ‘GHC.Real’
instance Num Integer -- Defined in ‘GHC.Num’
instance Ord Integer -- Defined in ‘integer-gmp:GHC.Integer.Type’
instance Read Integer -- Defined in ‘GHC.Read’
instance Real Integer -- Defined in ‘GHC.Real’
instance Show Integer -- Defined in ‘GHC.Show’
It's possible to add new instances and new typeclasses into the numeric type hierarchy. These are not fixed primitives. This means people have created alternative numeric preludes that adds extra functionality to the numeric system in Haskell. Check them out here:
- https://wiki.haskell.org/Numeric_Prelude
- https://wiki.haskell.org/Mathematical_prelude_discussion
- https://wiki.haskell.org/Libraries_and_tools/Mathematics
- https://hackage.haskell.org/package/numeric-prelude
One particular addition I like is the Data.Ratio
module. This adds a Rational
type alias, and the Ratio
type instance:
> :info Rational
type Rational = Ratio Integer -- Defined in ‘GHC.Real’
> :info Ratio
data Ratio a = !a GHC.Real.:% !a -- Defined in ‘GHC.Real’
instance Integral a => Enum (Ratio a) -- Defined in ‘GHC.Real’
instance Eq a => Eq (Ratio a) -- Defined in ‘GHC.Real’
instance Integral a => Fractional (Ratio a)
-- Defined in ‘GHC.Real’
instance Integral a => Num (Ratio a) -- Defined in ‘GHC.Real’
instance Integral a => Ord (Ratio a) -- Defined in ‘GHC.Real’
instance (Integral a, Read a) => Read (Ratio a)
-- Defined in ‘GHC.Read’
instance Integral a => Real (Ratio a) -- Defined in ‘GHC.Real’
instance Integral a => RealFrac (Ratio a) -- Defined in ‘GHC.Real’
instance (Integral a, Show a) => Show (Ratio a)
-- Defined in ‘GHC.Real’
Ratios allow one to write things "fractions" without evaluating them immediately.
Like 1/3
.
This also means that Haskell's numeric system won't always stay the same. It will evolve towards the future: http://www.reddit.com/r/haskell/comments/c9ynh/punt_the_prelude/
I used this https://github.com/benfred/venn.js
Plugged in this configration:
var sets = [
{sets: ['Num'], size: 60},
{sets: ['Ord'], size: 60},
{sets: ['Enum'], size : 60},
{sets: ['Fractional'], size: 20}, // Fractional is smaller than Num
{sets: ['Fractional', 'Num'], size: 20}, // Fractional(Num)
{sets: ['Fractional', 'Ord'], size: 7}, // RealFrac(Real(Num + Ord) + Fractional)
{sets: ['Fractional', 'Enum'], size: 0},
{sets: ['Floating'], size: 4}, // Floating is smaller than Fractional
{sets: ['Floating', 'Fractional'], size: 4},
{sets: ['Floating', 'Ord'], size: 2}, // RealFloat(Floating + RealFrac(Real(Num + Ord) + Fractional))
{sets: ['Ord', 'Num'], size: 14}, // Real(Num + Ord)
{sets: ['Enum', 'Num'], size: 10}, // Integral(Real(Num + Ord) + Enum)
{sets: ['Enum', 'Ord'], size: 10}
];