Is every class not a type in Haskell :
Prelude> :t max
max :: Ord a => a -> a -> a
Prelude> :t Ord
<interactive>:1:1: Not in scope: data constructor ‘Ord’
Prelude>
Why does this not print Ord
type signature ?
The Ord class is used for totally ordered datatypes. Instances of Ord can be derived for any user-defined datatype whose constituent types are in Ord . The declared order of the constructors in the data declaration determines the ordering in derived Ord instances.
What's a typeclass in Haskell? A typeclass defines a set of methods that is shared across multiple types. For a type to belong to a typeclass, it needs to implement the methods of that typeclass. These implementations are ad-hoc: methods can have different implementations for different types.
The Eq class defines equality ( == ) and inequality ( /= ). All the basic datatypes exported by the Prelude are instances of Eq , and Eq may be derived for any datatype whose constituents are also instances of Eq . The Haskell Report defines no laws for Eq .
An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.
Okay, there's a couple of things going on here.
First when you write :t Ord
you're looking for something called Ord
in the value namespace; specifically it would have to be a constructor, since the name starts with a capital letter.
Haskell keeps types and values completely separate; there is no relationship between the name of a type and the names of a type's constructors. Often when there's only one constructor, people will use the same name as the type. An example being data Foo = Foo Int
. This declares two new named entities: the type Foo
and the constructor Foo :: Int -> Foo
.
It's not really a good idea to think of it as just making a type Foo
that can be used both in type expressions and to construct Foo
s. Because also common are declarations like data Maybe a = Nothing | Just a
. Here there are 2 different constructors for Maybe a
, and Maybe
isn't a name of anything at all at the value level.
So just because you've seen Ord
in a type expression doesn't mean that there is a name Ord
at the value level for you to ask the type of with :t
. Even if there were, it wouldn't necessarily be related top the type-level name Ord
.
The second point that needs clarifying is that no, classes are not in fact types. A class is a set of types (which all support the interface defined in the class), but it is not a type itself.
In vanilla Haskell type classes are just "extra" things. You can declare them with a class
declaration, instantiate them with an instance
declaration, and use them in special syntax attached to types (the stuff left of the =>
arrow) as constraints on type variables. But they don't really interact with the rest of the language, and you cannot use them in the main part of a type signature (the stuff right of the `=> arrow).
However, with the ConstraintKinds
extension on, type classes do become ordinary things that exist in the type namespace, just like Maybe
. They are still not types in the sense that there can never be any values that have them as types, so you can't use Ord
or Ord Int
as an argument or return type in a function, or have a [Ord a]
or anything like that.
In that they are a bit like type constructors like Maybe
. Maybe
is a name bound in the type namespace, but it is not a type as such; there are no values whose type is just Maybe
, but Maybe
can be used as part of an expression defining a type, as in Maybe Int
.
If you're not familiar with kinds, probably ignore everything I've said from ConstraintKinds
onwards; you'll probably learn about kinds eventually, but they're not a feature you need to know much about as a beginner. If you are, however, what ConstraintKinds
does is make a special kind Constraint
and have type class constraints (left of the =>
arrow) just be ordinary type-level things of kind Constraint
instead of special purpose syntax. This means that Ord
is a type-level thing, and we can ask it's kind with the :k
command in GHCI:
Prelude> :k Ord
* -> Constraint
Which makes sense; max
had type Ord a => a -> a -> a
, so Ord a
must have kind Constraint
. If Ord
can be applied to an ordinary type to yield a constraint, it must have kind * -> Constraint
.
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