[Haskell-beginners] question on types

Daniel Fischer daniel.is.fischer at googlemail.com
Fri Jul 29 18:50:25 CEST 2011


On Friday 29 July 2011, 18:14:30, Jake Penton wrote:
> On 2011-07-29, at 11:22 AM, Brandon Allbery wrote:
> > Ask yourself this: is True *every* instance of Ord?  You are expecting
> > it to be an "any", but it's an "every" (forall).
> > 
> > By the way, True happens to be an instance of Ord but it doesn't have
> > to be.  You're working backwards here, I think.  It happens that
> > useful operations on things in class Ord generally produce Bool; that
> > doesn't mean Bool must be Ord.
> 
> Right - actually, I did not expect it to compile. I gave the code as,
> perhaps, a counterexample to what some other responses seemed to be
> asserting, although it is possible I did not get their point(s)
> correctly. They seemed to say that adding a constraint is of itself
> what makes the cases that used numeric literals work.

Given the fact that per the report a numeric literal is interpreted as a 
call to fromInteger or fromRational (depending on whether it's an integer 
literal or a fractional literal), the addition of the constraint is all 
that is needed to make it work, since by those functions' types, the value 
represented by a numeric literal is polymorphic.

f = 1

is really

f = fromInteger 1&

(where n& stands for the Integer of the appropriate value, it's not 
Haskell; in GHC [with integer-gmp], it would be

f = fromInteger (S# 1#)

where S# is a constructor of Integer [MagicHash extension required to use 
it] and 1# is a literal of type Int#, raw signed machine int, four or eight 
bytes of raw memory).

True, on the other hand, is a monomorphic value of type Bool, it cannot 
have any other type than Bool and trying to specify any other type for it 
leads to a compile time error.

> But it seems (as
> described in Brent Yorgey's post) that there is more to it than that.

The fundamental difference is monomorphic vs. polymorphic expressions.
A monomorphic expression like [True] has a specific type, it doesn't make 
sense to add constraints to it (but it makes sense to ask whether 
constraints are satisfied for using it).
A polymorphic expression like (toEnum 0) can have many types, but in 
general the types it can have are constrained by the types of 
subexpressions/used functions. There is a minimal set of constraints you 
need to specify, e.g.

l = [toEnum 0, 3]

needs the constraints (Enum a) and (Num a) in the type signature

l :: (Num a, Enum a) => [a]

but you can use more or more restrictive constraints to specify a less 
general type than it could have, for example

l :: (Enum a, Bounded a, Num a, Read a) => [a]

or

l :: (Enum a, RealFrac a) => [a]

are valid signatures since they are less general/more constrained than the 
first one.
You could also give a monomorphic signature, l :: [Double], which will 
compile if and only if the specified type of list elements belongs to Num 
and Enum.



More information about the Beginners mailing list