[mercury-users] Subtyping for Mercury?

David Glen JEFFERY dgj at cs.mu.oz.au
Tue Apr 15 14:59:11 AEST 1997

Ralph Becket wrote:
> Hi,
> has anybody considered augmenting the Mercury type scheme with a
> subtyping relation a la Modula 3?  This would be very handy for
> implementing OO support for Mercury.

We are in the process of adding type class, like those in Haskell or Gofer,
to the type system. This should make it easier to take advantage of some
OOP techniques in Mercury.

Type classes allow the programmer to (among other things...)
    - define an interface to which a type must conform in order to be a member
      of the class (`class' or `typeclass' declarations)
    - define how a type satisfies a class interface (`instance' declarations)
    - declare that one class is a sub-class of another (by adding constraints to
      a class declaration)
    - restrict polymorphic arguments of preds/functions to be members of a
      particular class. eg. an argument may be of type `anything, as long as
      it has a certain set of operations defined on it'.

I will give an example which approximately satisfies the example you gave:
(Don't take the syntax as gospel --- we are yet to decide on this. I have used
a mixture of Haskell and Mercury here).

> Let's say I have a class foo with attributes foo_a_1 to foo_a_3 and
> methods foo_m_1 to foo_m_4 (sorry about the boring names).

:- typeclass foo(T) where
    % functions to access and set foo_a_1 and foo_a_3

    % Now declare the methods
    pred foo_m_1(type1::in, type2::out) is nondet,

    pred foo_m_3(type3, int, char),
    mode foo_m_3(in, out, in) is det

    % It is also possible to include default definitions for the methods in
    % terms of the other methods. The exact details of this are still a little
    % hazy

> Now I want a subclass bar which inherits all of the above, but also
> adds attributes bar_a_1 to bar_a_2 and methods bar_m_1 to bar_m3 and
> overrides foo_m_1 with bar_m_4.

    % We now use the superclass mechanism: we say that for `T' to be a member
    % of bar, it must already be a member of foo. Thus if `T' is a member of
    % bar, it also has access to the foo methods.
:- typeclass foo(T) ---> bar(T) where
    % more access functions for bar_a_1 and bar_a_2.

    % declarations of the new methods
    pred bar_m_1(...).

Achieving the overloading is a little tricky. Probably the best way to do it
is by declaring a type to be an instance of the class, and overriding the
default definition (although default definitions are a somewhat contentious
issue). eg.

:- instance foo(some_type) where
    % definitions of the methods for foo

        % Override the default definition.
    foo_m_1 == bar_m_4.

> Anything which accepts a foo as a class should also accept a bar (but
> not vice versa).  Moreover, there should be a way of narrowing the type
> of a bar object in order to access the hidden foo methods.

This is achieved by using a `context' in a predicate --- ie. by placing 
constraints upon type variables eg:

    % T must be in the class `foo'. `foo' methods can be used upon things of
    % type T.
:- pred baz(T::in, int::out) is det.

    % T must be in the class `bar'. `bar' methods can be used upon things of
    % type T, as can `foo' (because of the class hierarchy).
:- pred kabam(T::in, int::out) is semidet.

As you can see, there is not a direct mapping between the type class concepts
and the OO concepts you mentioned. The main reason for this is that type classes
separate the notions of interface and implementation. These manifest themselves
as `typeclasses' and `types' respectively. This is in contrast to, for example,
C++, in which there is only one such thing --- the `class'.

love and cuddles,

This .sig deliberately left blank

More information about the users mailing list