[mercury-users] Newbie questions about typeclasses

Critterrathman at aol.com Critterrathman at aol.com
Tue Apr 24 16:05:17 AEST 2001


First, a disclaimer.  I've been toying around with polymorphism as understood 
in an OOP sense in a number of different languages.  I realize that Mercury 
isn't geared towards such things, but I use it as a method to learn about 
various programming languages.  Mercury is definitely unique in what it tries 
to do, so I've been trying off and on to learn what I can about the language.

I've been playing around with Mercury for a while, and just haven't quite got 
a handle on the syntax of typeclasses (not to mention some of the major 
concepts of the language).  I don't really know enuf about the language to 
frame my questions in a coherent manner, but I figure the only way to learn 
is to ask dumb questions.  I apologize for not making the questions a bit 
more straightforward but hopefully the code is fairly easy to understand (at 
least that was the intention).  :-)

The code I'm trying to construct is the toy example of shape inheritance 
(circle extends shape, etc...).  I include what little I've been able to 
accomplish at the end of this message (sorry for the long post).  A couple of 
issues that I've had at this point are:


1).  For some reason, MMC complains about not being able to find shape.int3.  
I am trying to play with a sort of limited inheritance in this situation 
where the circleClass inherits the method signatures of the shapeClass.  If I 
manually copy the file shape.int2 into the requested file shape.int3, 
everything seems to work fine.  I don't know enuf about the compiler but a 
wild guess would be that each level of inheritance that is split across 
modules is adding one to the index of the module file.  Of course that's a 
WAG.  Anyhow, is there a reason why MMC exhibits this behavior?

      Here's the calls to MMC for the interface generate:

            mmc --make-int shape.m
            mmc --make-int circle.m

      Here's the error message I get when it tries to create the interface 
for circle.m:

            --: not found
            mercury_compile: can't open file `shape.int3'.
            Error reading short interface files.
            `circle.int' and `circle.int2' not written.

      If I change the calls to the following, I get no complaints:

            mmc --make-int shape.m
            cp shape.int2 shape.int3
            mmc --make-int circle.m


2).  I'm not sure how to get the methods for the typeclass to be used.  What 
I wanted is to just expose the record and the typeclass definitions in the 
interface and then map the specific function to the typeclass within the 
implementation section of the module.  This would mean that the interface 
might expose a method called 'getRadius' while the local method can be 
implemented locally as 'getRadius_Circle'.  If I try to access the method by 
the name of the typeclass method, I get an error message about an unsatisfied 
constraint.

      Here's the call in the main routine that causes the problem:

            draw_Circle(setRadius(ACircle, 30))

      Here's the error message:

            In clause for predicate `polymorph:main/2':
              unsatisfiable typeclass constraint(s):
              `circle:circleClass((circle:circleRecord))'.

      However, if I don't use the typeclass and directly go to the defined 
function, it works:

            draw_Circle(setRadius_Circle(ACircle, 30))


I think I'm missing something obvious about the way that typeclasses work, 
but I thought the purpose was to map method names from local implementation 
names into a globally recognized method name.  The mapping of the class 
method names into the implementation names is taken care of by the instance 
declarations.  Just wondering what I am missing here?

Along these lines, I really didn't want to put the local implementation mode 
into the interface section.  If I could get the method names to map off of 
the class, I'd probably be able to push the local names into the 
implementation section.


3).  Also along similar lines, I was having difficulty getting the compiler 
to accept pred(draw/3) in the instance declaration.  The pred() compiles fine 
in the typeclass statement but it complains if I try to map it within the 
instance.  I see in the documentation that it is allowed to mix predicates 
and functions within the instance declarations.  The only proviso that I see 
is that all the example predicates take a single constraint.  In this case, 
the pred is doing some simple io so it requires the single argument as well 
as the two tag along arguments for input/output.  In the code that compiles 
and runs, I just commented out the line in the instance declaration:

      Here's the line of code that causes the problem:

            :- instance shapeClass(circleRecord) where [
               pred(draw/3) is draw_Circle,

      Here's the error message that I get:

            In instance declaration for `shape:shapeClass/1': incorrect
              method name(s): predicate `shape:draw/3' .

Anyhow, what exactly am I doing wrong here?


Thanks in advance for any help.

Here's the code as it currently runs.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% polymorph.m %%%%%%%%%%%%%%%%%%%%%%%%%%%
:- module polymorph.

:- interface.
:- import_module io, int, list, shape, circle.
:- pred main(io__state, io__state).
:- mode main(di, uo) is det.

:- implementation.

main -->
   { Scribble = [circleRecord(10, 20, 5), circleRecord(15, 25, 8)] },
   { ACircle = circleRecord(0, 0, 15) },
   drawLoop(Scribble),
   draw_Circle(setRadius_Circle(ACircle, 30)).

:- pred drawLoop(list(circleRecord), io__state, io__state).
:- mode drawLoop(in, di, uo) is det.
drawLoop([]) --> [].
drawLoop([Hd | Tl]) -->
   draw_Circle(Hd),
   draw_Circle(rMoveTo_Circle(Hd, 100, 100)),
   drawLoop(Tl).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%% shape.m %%%%%%%%%%%%%%%%%%%%%%%%%%%
:- module shape.

:- interface.
:- import_module io, int.

% declare method interfaces for the shape superclass
:- typeclass shapeClass(This) where [
   pred draw(This, io__state, io__state),
   func getX(This) = int,
   func getY(This) = int,
   func setX(This, int) = This,
   func setY(This, int) = This,
   func moveTo(This, int, int) = This,
   func rMoveTo(This, int, int) = This
].

:- implementation.


%%%%%%%%%%%%%%%%%%%%%%%%%%%%% circle.m %%%%%%%%%%%%%%%%%%%%%%%%%%%
:- module circle.

:- interface.
:- import_module io, int, shape.

% declare the constructor for circle class
:- type circleRecord
   ---> circleRecord(
      x :: int,
      y :: int,
      radius :: int
   ).

% declare method interfaces for circle subclass
:- typeclass circleClass(This) <= shapeClass(This) where [
   func getRadius(This) = int,
   func setRadius(This, int) = circleRecord
].


:- pred draw_Circle(circleRecord, io__state, io__state).
:- mode draw_Circle(in, di, uo) is det.

:- func getX_Circle(circleRecord) = int.
:- mode getX_Circle(in) = out is det.

:- func getY_Circle(circleRecord) = int.
:- mode getY_Circle(in) = out is det.

:- func getRadius_Circle(circleRecord) = int.
:- mode getRadius_Circle(in) = out is det.

:- func setX_Circle(circleRecord, int) = circleRecord.
:- mode setX_Circle(in, in) = out is det.

:- func setY_Circle(circleRecord, int) = circleRecord.
:- mode setY_Circle(in, in) = out is det.

:- func setRadius_Circle(circleRecord, int) = circleRecord.
:- mode setRadius_Circle(in, in) = out is det.

:- func moveTo_Circle(circleRecord, int, int) = circleRecord.
:- mode moveTo_Circle(in, in, in) = out is det.

:- func rMoveTo_Circle(circleRecord, int, int) = circleRecord.
:- mode rMoveTo_Circle(in, in, in) = out is det.

:- implementation.

% map the circle methods for shape superclass
:- instance shapeClass(circleRecord) where [
   %  pred(draw/3) is draw_Circle,
   func(getX/1) is getX_Circle,
   func(getY/1) is getY_Circle,
   func(setX/2) is setX_Circle,
   func(setY/2) is setY_Circle,
   func(moveTo/3) is moveTo_Circle,
   func(rMoveTo/3) is rMoveTo_Circle
].

% map the methods for circle subclass
:- instance circleClass(circleRecord) where [
   func(getRadius/1) is getRadius_Circle,
   func(setRadius/2) is setRadius_Circle
].

% method definitions for the circle class follow
getX_Circle(This) = This^x.
getY_Circle(This) = This^y.
getRadius_Circle(This) = This^radius.
setX_Circle(circleRecord(_,Y,Radius), X) = circleRecord(X, Y, Radius).
setY_Circle(circleRecord(X,_,Radius), Y) = circleRecord(X, Y, Radius).
setRadius_Circle(circleRecord(X,Y,_), Radius) = circleRecord(X, Y, Radius).
moveTo_Circle(circleRecord(_,_,Radius), X, Y) = circleRecord(X, Y, Radius).
rMoveTo_Circle(circleRecord(X,Y,Radius), DX, DY) = circleRecord(X+DX, Y+DY, 
Radius).

draw_Circle(This) -->
   io__write_string("Drawing a circle at:("),
   io__write_int(getX_Circle(This)),
   io__write_string(","),
   io__write_int(getY_Circle(This)),
   io__write_string("), radius "),
   io__write_int(getRadius_Circle(This)),
   io__nl.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurylang.org/archives/users/attachments/20010424/49047883/attachment.html>


More information about the users mailing list