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
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With