Typeclass constraints may also be placed upon instance declarations. The arguments of such constraints 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 be type variables that appear in the types in the instance declaration.
For example, consider the following declaration of a type class of types that may be printed:
:- typeclass portrayable(T) where [ pred portray(T::in, io.state::di, io.state::uo) is det ].
The programmer could declare instances such as
:- instance portrayable(int) where [ pred(portray/3) is io.write_int ]. :- instance portrayable(char) where [ pred(portray/3) is io.write_char ].
However, when it comes to writing the instance declaration for a type such as
list(T), we want to be able print out the list elements using the
portray/3 for the particular type of the list elements. This can be
achieved by placing a type class constraint on the
as in the following example:
:- instance portrayable(list(T)) <= portrayable(T) where [ pred(portray/3) is portray_list ]. :- pred portray_list(list(T), io.state, io.state) <= portrayable(T). :- mode portray_list(in, di, uo) is det. portray_list(, !IO). portray_list([X | Xs], !IO) :- portray(X, !IO), io.write_char(' ', !IO), portray_list(Xs, !IO).
For abstract instance declarations, the type class constraints on an abstract instance declaration must exactly match the type class constraints on the corresponding non-abstract instance declaration that defines that instance.