Reputation: 445
Could someone please explain why this compiles
Prelude> 1 :: Num a => a
and this doesn't
Prelude> 1.0 :: Num a => a
Second example would work with Fractional
, but Num
is superclass of Fractional
. Just like it's superclass of Integral
.
Upvotes: 1
Views: 150
Reputation: 120731
It's important to distinguish between polymorphism in OO languages and polymorphism in Haskell. OO polymorphism is covariant, while Haskell's parametric polymorphism is contravariant.
What this means is: in an OO language, if you have
class A {...}
class B: A {...}
i.e. A
is a superclass of B
, then any value of type B
is also a value of type A
. (Note that any particular value is actually not polymorphic but has a concrete type!) Thus, if you had
class Num {...}
class Fractional: Num {...}
then a Fractional
value could indeed be used as a Num
value. That's roughly what covariant means: any subclass value is also a superclass value; the values hierarchy goes the same direction as the type hierarchy.
In Haskell, class
es are different. There is no such thing as a “value of type Num
”, only values of concrete types a
. That type may be in the Num
class.
Unlike in OO languages, a value like 1 :: Num a => a
is polymorphic: it can take on whatever type the environment demands, provided the type is in the Num
class. (Actually that syntax is just shorthand for 1 :: ∀ a . Num a => a
, to be read as “for all types a
, you can have a value 1
of type a
.) For example,
Prelude> let x = 1 :: Num a => a
Prelude> x :: Int
1
Prelude> x :: Double
1.0
You can also give x
a more specific constraint of Fractional
, since that's a subclass of Num
. That just restricts what type the polymorphic value can be instantiated to:
Prelude> let x = 1 :: Fractional a => a
Prelude> x :: Int
<interactive>:6:1:
No instance for (Fractional Int) arising from a use of ‘x’
...
Prelude> x :: Double
1.0
because Int
is not a fractional type.
Thus, Haskell's polymorphism is contravariant: polymorphic values restricted to a superclass can also be restricted to a subclass instead, but not the other way around. In particular, you can obviously have
Prelude> let y = 1.0 :: Fractional a => a
(y
is the same as x'
), but you can not generalise this to y' = 1.0 :: Num a => a
. Which is a good thing as Ingo remarked since otherwise it would be possible to do
Prelude> 3.14159 :: Int
????
Upvotes: 3
Reputation: 36349
The superclass relationship between type classes does not establish relationships between types. It tells us only that if type t is a member of type class C, and B is a super class of C, then t will also be member of B.
It doesn't say that each value v::t can be used at any type that is also member of B. But this is what you are stating with:
3.14159 :: Num a => a
Upvotes: 3
Reputation: 116174
If we have
x :: Num a => a
the user of x
can pick a
as wanted. E.g.
x :: Int
What would this evaluate to if x = 1.5
?
For this reason a floating point literal can't be given the polytype Num a => a
, since its value won't (in general) fit all Num
eric types.
Integral literals instead fit every numeric type, so they are allowed.
Upvotes: 5