Type class constraints may also appear in
meaning that one type class is a “superclass” of another.
The arguments of a constraint on a type class declaration must be either type variables or ground types. Each constraint must contain at least one variable argument and all variables that appear in the arguments must also be arguments to the type class in question.
For example, the following declares the ‘ring’ type class, which describes types with a particular set of numerical operations defined:
:- typeclass ring(T) where [ func zero = (T::out) is det, % '+' identity func one = (T::out) is det, % '*' identity func plus(T::in, T::in) = (T::out) is det, % '+'/2 (forward mode) func mult(T::in, T::in) = (T::out) is det, % '*'/2 (forward mode) func negative(T::in) = (T::out) is det % '-'/1 (forward mode) ].
We can now add the following declaration:
:- typeclass euclidean(T) <= ring(T) where [ func div(T::in, T::in) = (T::out) is det, func mod(T::in, T::in) = (T::out) is det ].
This introduces a new type class,
euclidean, of which
ring is a
superclass. The operations defined by the
euclidean type class are
mod, as well as all those defined by the
type class. Any type declared to be an instance of
euclidean must also
be declared to be an instance of
Type class constraints on type class declarations gives rise to a superclass relation. This relation must be acyclic. That is, it is an error if a type class is its own (direct or indirect) superclass.