Next: Abstract typeclass declarations, Previous: Typeclass declarations, Up: Type classes [Contents]

Once the interface of the type class has been defined in the `typeclass`

declaration, we can use an `instance`

declaration to define how a
particular type (or sequence of types) satisfies the interface declared
in the `typeclass`

declaration.

An instance declaration has the form

:- instanceclassname(typename(typevar, …), …) where [methoddefinition,methoddefinition, …].

An ‘`instance`’ declaration gives a type for each parameter of the
type class. Each of these types must be either a type with no arguments, or
a polymorphic type whose arguments are all type variables.
For example `int`

, `list(T)`

, `bintree(K, V)`

and
`bintree(T, T)`

are allowed, but `T`

and `list(int)`

are not.
The types in an instance declaration must not be abstract types which
are elsewhere defined as equivalence types.
A program may not contain more than one instance declaration for a particular
type (or sequence of types, in the case of a multi-parameter type class) and
typeclass.
These restrictions ensure that there are no overlapping instance declarations,
i.e. for each typeclass there is at most one instance declaration that may be
applied to any type (or sequence of types).

Each `methoddefinition` entry in the ‘`where […]`’ part of an
`instance`

declaration defines the implementation of one of the class
methods for this instance.
There are two ways of defining methods.
The first way is to define a method by giving the name of the predicate or
function which implements that method.
In this case, the `methoddefinition` must have one of the following forms:

pred(methodname/arity) isprednamefunc(methodname/arity) isfuncname

The `predname` or `funcname` must name a predicate or
function of the specified arity whose type, modes, determinism, and
purity are at least as permissive as the declared type, modes,
determinism, and purity of the class method with the specified
`methodname` and `arity`, after the types of the arguments
in the instance declaration have been substituted in place of the
parameters in the type class declaration.

The second way of defining methods is by listing the clauses for the
definition inside the instance declaration. A `methoddefinition`
can be a clause. These clauses are just like the clauses used to
define ordinary predicates or functions (see Items), and so they
can be facts, rules, or DCG rules. The only difference is that in instance
declarations, clauses are separated by commas rather than being terminated
by periods, and so rules and DCG rules in instance declarations must
normally be enclosed in parentheses. As with ordinary predicates,
you can have more than one clause for each method. The clauses must
satisfy the declared type, modes, determinism and purity for the
method, after the types of the arguments in the instance declaration
have been substituted in place of the parameters in the type class
declaration.

These two ways are mutually exclusive: each method must be defined
either by a single naming definition (using the ‘`pred(…) is
predname`’ or ‘

Here’s an example of an instance declaration and the different kinds of method definitions that it can contain:

:- typeclass foo(T) where [ func method1(T, T) = int, func method2(T) = int, pred method3(T::in, int::out) is det, pred method4(T::in, io.state::di, io.state::uo) is det, func method5(bool, T) = T ]. :- instance foo(int) where [ % method defined by naming the implementation func(method1/2) is (+), % method defined by a fact method2(X) = X + 1, % method defined by a rule (method3(X, Y) :- Y = X + 2), % method defined by a DCG rule (method4(X) --> io.print(X), io.nl), % method defined by multiple clauses method5(no, _) = 0, (method5(yes, X) = Y :- X + Y = 0) ].

Each ‘`instance`’ declaration must define an implementation for
every method declared in the corresponding ‘`typeclass`’ declaration.
It is an error to define more than one implementation for the same
method within a single ‘`instance`’ declaration.

Any call to a method must have argument types (and in the case of functions, return type) which are constrained to be a member of that method’s type class, or which match one of the instance declarations visible at the point of the call. A method call will invoke the predicate or function specified for that method in the instance declaration that matches the types of the arguments to the call.

Note that even if a type class has no methods, an explicit instance declaration is required for a type to be considered an instance of that type class.

Here’s an example of some code using an instance declaration:

:- type coordinate ---> coordinate( float, % X coordinate float % Y coordinate ). :- instance point(coordinate) where [ pred(coords/3) is coordinate_coords, func(translate/3) is coordinate_translate ]. :- pred coordinate_coords(coordinate, float, float). :- mode coordinate_coords(in, out, out) is det. coordinate_coords(coordinate(X, Y), X, Y). :- func coordinate_translate(coordinate, float, float) = coordinate. coordinate_translate(coordinate(X, Y), Dx, Dy) = coordinate(X + Dx, Y + Dy).

We have now made the `coordinate`

type an instance of the `point`

type class. If we introduce a new type, `coloured_coordinate`

which
represents a point in two dimensional space with a colour associated with it,
it can also become an instance of the type class:

:- type rgb ---> rgb( int, int, int ). :- type coloured_coordinate ---> coloured_coordinate( float, float, rgb ). :- instance point(coloured_coordinate) where [ pred(coords/3) is coloured_coordinate_coords, func(translate/3) is coloured_coordinate_translate ]. :- pred coloured_coordinate_coords(coloured_coordinate, float, float). :- mode coloured_coordinate_coords(in, out, out) is det. coloured_coordinate_coords(coloured_coordinate(X, Y, _), X, Y). :- func coloured_coordinate_translate(coloured_coordinate, float, float) = coloured_coordinate. coloured_coordinate_translate(coloured_coordinate(X, Y, Colour), Dx, Dy) = coloured_coordinate(X + Dx, Y + Dy, Colour).

If we call ‘`translate/3`’ with the first argument having type
‘`coloured_coordinate`’, this will invoke
‘`coloured_coordinate_translate`’.
Likewise, if we call ‘`translate/3`’ with the first argument having type
‘`coordinate`’, this will invoke ‘`coordinate_translate`’.

Further instances of the type class could be made, e.g. a type that represents the point using polar coordinates.

Next: Abstract typeclass declarations, Previous: Typeclass declarations, Up: Type classes [Contents]