[mercury-users] errors with typeclasses

Ralph Becket rbeck at microsoft.com
Thu Nov 2 04:30:19 AEDT 2000


>From david wallin on 01/11/2000 15:39:53
> 
> Ok, I have done some changes (inspired by your replies) and the error 
> list is now shorter. But let me explain a bit what I'm trying to do:
> 
> I want the population typeclass to contain chromosomes. A population 
> should probably contain the same kind of chromosomes (but I'm not 
> sure how to make this constraint) but this is not really that 
> important right now.
> Basically, I want to use typeclasses in the same way as I use 
> interfaces in Java but without the limitations.
> 
> I want instances of population to be any structure and if possible, I 
> would like to be able to use already existing structure 
> implementations (ie not tailor-made to be used with the population 
> typeclass).
> 
> The remaining error messages (at the end of this email), would I need 
> multi-parameter typeclasses or constructor classes to get rid of this 
> ?

I've had a lot of grief with typeclasses, but I think I've got a
handle on them now.  Let's see... 
> 
> Ok, here's the code and error messages:
> 
> 	:- typeclass chromosome(C)
> 		where [
> 		       func mutate_chromosome(C) = C,
> 		       func gene(C, int) = G <= gene(G),
> 		       pred set_gene(C, G) <= gene(G)
> 		      ].

I can see a problem already.

(1) func gene(C, int) = G <= gene(G)

The implicit mode here is `gene(in, in) = out is det'.
However, G is (implicitly) universally quantified, so
this means that method gene/2 has to be able to produce the
right result type in any context expecting something
implementing gene/1.

This is clearly not on since that could be any type
at all, while gene/2 can only have one actual return
type (modulo polymorphism).

There are two solutions.

(a) use existential types:

	some [G] func gene(C, int) = G => gene(G)

which says that calling the method gene/2 will return
you an object *of some unknown type* that is guaranteed
to implement the gene/1 typeclass.

This would tie in with the next method declaration,

	pred set_gene(C, G) <= gene(G)

which accepts *any type at all* that happens to implement
the gene/1 typeclass.

(b) use multi-parameter typeclasses:

:- typeclass chromosome(C) where [
	func mutate_chromosome(C) = C
].

:- typeclass chromosome_gene(C, G) <= (chromosome(C), gene(G)) where [
	func gene(C, int) = G,
	pred set_gene(C, G)	% What are the modes, here?
].

Mystical typing considerations mean you can't mix C & G signatures
up in the same typeclass as C-only signatures.

> 	:- typeclass population(P)
> 		where [
> 		       pred add_chromosome(P, C1, P) <= chromosome(C1),
> 		       mode add_chromosome(in, in, out) is det,

Aside: you can use more conventional func notation here...

	func add_chromosome(P, C1) = P <= chromosome(C1)

> 		       pred remove_chromosome(P, C2, P) <= chromosome(C2),
> 		       mode remove_chromosome(in, in, out) is det,
> 
> 		       some [C3] func get_chromosome(P, int) = C3 =>
> 				chromosome(C3)
> 		      ].
> 
> 
> 	:- instance population(list(D)) <= chromosome(D)
> 		where [
> /*line 83*/	       pred(add_chromosome/3) is list_add_chromosome,
> /*line 84*/	       pred(remove_chromosome/3) is list_remove_chromosome,
> 		       func(get_chromosome/2) is list_get_chromosome
> 		      ].
> 
> 
> %
> % list_add_chromosome
> 
> 	:- pred list_add_chromosome(list(T), T, list(T)) <= chromosome(T).
> 	:- mode list_add_chromosome(di, in, uo) is det.

Right, in the population/1 typeclass you promised that add_chromosome/3
should work for *any* 2nd arg. that implements chromosome/1.  This
pred, on the other hand, works *only* for objects of the same type as
the list members.

[Also, your modes in list_add_chromosome/3 are more constrained than
those in the typeclass method.  Naughty.]

If you go for the existential types approach ((a) above) then you
can't just have list(T) as an instance of chromosome/1.  Instead
you need to do something like this:

:- type some_chromosome ---> some [C] some_chromosome(C) => chromosome(C).
:- type some_chromosomes == list(some_chromosome).
:- instance population(some_chromosomes) where [...].

Nooooooooo!  This won't work since, according to the reference manual,
instance arguments have to be of the type `typename(typevar, ...)'
and `some_chromosomes' expands to `list(some_chromosome)' which
 violates the rule.  Instead we need to do something unpleasant and
use a no-tag type:

:- type some_chromosomes ---> some_chromosomes(list(some_chromosome)).

Now the instance declaration will work.


If you want to take the multi-parameter typeclass approach ((b) above) 
and do away with existentially quantified types, then you have to declare

:- instance chromosome(list(my_gene)) where [...].
:- instance chromosome_gene(list(my_gene), my_gene) where [...].

Oh dear, `list(my_gene)' isn't of the form `typename(typevar, ...)'.
Hmm, we now have to jump through a hoop or two:

:- type list_of_my_genes ---> list_of_my_genes(list(my_gene)).
:- instance chromosome(list_of_my_genes) where [...].
:- instance chromosome_gene(list_of_my_genes, my_gene) where [...].

We need the (no-tag) wrapper since the compiler will see through
sneaky tricks like using equivalence types.  And we have to do this
for every `my_gene' type.

As you can see, this is a right pain in the bum.

Once we get constructor classes and functional dependencies into the
typeclass mechanism (pray God let it be soon) then these problems
will disappear and we will all be happy bunnies.

> 	list_add_chromosome(ListIn, Chromosome, ListOut) :-
> 		ListOut = [Chromosome | ListIn].
> 
> 
> (I tried without the 'chromosome(T)' constraint with the same
> effect).
> 
> 
> gusga.m:084: In clause for type class method implementation:
> gusga.m:084:   in argument 2 of call to predicate
`list_remove_chromosome/3':
> gusga.m:084:   type error: variable `HeadVar__2' has type `C2',
> gusga.m:084:   expected type was `D'.
> gusga.m:083: In clause for type class method implementation:
> gusga.m:083:   in argument 2 of call to predicate `list_add_chromosome/3':
> gusga.m:083:   type error: variable `HeadVar__2' has type `C1',
> gusga.m:083:   expected type was `D'.
> For more information, try recompiling with `-E'.

DEVELOPERS: this error message looks misleading - I can't see
any predicate *calls* involved.

Hope this helps,

Ralph

--
Ralph Becket      |      MSR Cambridge      |      rbeck at microsoft.com 

--------------------------------------------------------------------------
mercury-users mailing list
post:  mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the users mailing list