views:

170

answers:

2

In Haskell, why would you define a function with a type constraint:

ghci> :t (==)  
(==) :: (Eq a) => a -> a -> Bool

Rather than defining it so it's type was:

ghci> :t (==)  
(==) :: Eq -> Eq -> Bool
+8  A: 

You wouldn't do the second version because you'd get a compile error. Eq is not a type, it's a typeclass. You can't use a typeclass in places where a type is required.

If you did define your own type MyEq and then define a function == with the type MyEq -> MyEq -> Bool, the expression "hello" == "hello" would be invalid because "hello" is a value of type String and not of type MyEq. Since there is no subtyping in haskell a value couldn't be of type String and of type MyEq at the same time.

So if you want to define a function that can accept values of different types which meet certain conditions, you need type classes.

sepp2k
To follow up to that, see the error you get from running `let a :: Eq -> Eq -> Bool ; a b c = b == c ; in a 2 2` in ghci.
Aidan Cully
Thanks guys, that straightens things out!
sneeu
+7  A: 

In Haskell, a "type" can only have a single specific set of possible values that does not overlap with any other type. There is no such thing as one type being "a different kind of" or "a subtype of" another type.

When we want polymorphism, i.e., functions that can apply to more than one type, we can specify that by using a type variable in the type signature of the function.

But a type variable can refer to any type at all. We don't always know how to define our function for absolutely every type. For example, the (>) function only makes sense for types whose elements are comparable. The compiler will reject a function whose type signature is too general, in order to help us avoid writing gibberish.

In your example, Eq is not a type. It is a typeclass - a name for a set of types. We declare typeclass names using the class keyword, and add types into the class using the instance keyword. The purpose of a typeclass is to be used in a constraint to limit the scope of a type variable.

The Haskell approach to types and polymorphism is based on the "Hindley-Milner type system". It is an extremely precise yet very expressive way of describing data that makes it easier to give the compiler a huge amount of intelligence about the types in your program. That intelligence helps the compiler to infer types automatically, to give you a lot of help in getting your program correct, and to optimize the compiled result, among other benefits.

But be careful - it is very different than the way types are used in OOP, which may be what you are used to. There is usually no direct translation between an OO program and a Haskell program. You have to think about the task in a different way right from the start. Be especially careful not to confuse Haskell's concepts of "class" and "instance" with the completely different way those words are used in OOP.

Yitz
"it is very different than the way types are used in OOP", I disagree. Haskell typeclasses are really like "abstract classes" or "interfaces" in OOP, and I suspect some vtable is passed along with the arguments to a polymorphic function for the runtime dispatch to take place.
Alexandre C.