rodic
rodic

Reputation: 445

Setting type of fractional to num

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

Answers (3)

leftaroundabout
leftaroundabout

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, classes 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

Ingo
Ingo

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

chi
chi

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 Numeric types.

Integral literals instead fit every numeric type, so they are allowed.

Upvotes: 5

Related Questions