[mercury-users] Present state of type classes - especially for parametrized types
Jörg Roman Rudnick
joerg.rudnick at t-online.de
Tue Oct 3 21:27:24 AEST 2006
Hi Ralph,
it will be a pleasure to me, here an interesting quote of yourself:
http://www.mercury.cs.mu.oz.au/mailing-lists/mercury-developers/mercury-developers.0009/0216.html
Another thread on this behalf was initiated by Michael Day:
http://www.mercury.cs.mu.oz.au/mailing-lists/mercury-users/mercury-users.9911/0012.html
A relevant example of Russell/Norvig (AI - A Modern Approach) could be a
fringe for a search algorithm, which for instance could be FIFO or LIFO
buffer. To my present understanding, an aim of typeclasses is to be able
to provide type-dependent behaviour.
This surely is already possible with tricky discrimitated unions, but at
the price that all behaviours most be expressed at a central place -
introducing a new behaviour upon a new type needs changing this code base.
Type classes, on the other hand, allow defining new instances without
change of type class code and even the type class user code - one just
passes a new type which is freshly instanced. Also, the syntax seems to
be intended to be more elegant - one extracts as much info as possible
out of the instance's type lore...
When passing buffer instances on behalf of the example above, these may
well contain info about the content type (e.g., intNode vs. stringNode)
as well as the buffer behaviour (e.g., fifo(...) vs. lifo(...)) - a job
for type classes?
Although the mailing list threads were not too encouring, my previous
experiences with the expressive power of Mercury were so motivating I
decided to give it a try anyway. You yourself noted the two issues that
seem relevant to me:
(1) 'O-tone R.B.':
> The empty/0 func causes us some grief, since it doesn't mention T;
instead
> we need to jump through a hoop like
> func empty(T) = S, % T is a dummy arg to make the types work
> mode empty(unused) = out is det,
> which isn't very satisfying.
ASSUMED STATE: Impossible at the moment...
Here you seemingly address what the OO world calls factory methods - as
such usually are only needed once at instantiation, one can live without
it for a long time... :-)
(2) 'O-tone R.B.' (others discussed similar ideas in the following):
> But things get worse: in an instance declaration the parameters must
all be of the form
> `typename(typevar, ...)', and not just `typevar', which
> rules out
> :- instance sequence(list(T), T).
> Oh deary me! What is needed, as was discussed at the end of last year,
> is the ability to handle constructor classes, so we could write
> :- typeclass sequence(S(T)) where [
> func empty = S(T),
> func cons(T, S(T)) = S(T),
> func head(S(T)) = T,
> func tail(S(T)) = S(T),
> ...
> ].
> and
> :- instance sequence(list(T)).
ASSUMED STATE: Difficult to say...
While the compiler seems to tolerate parametrized types in typeclasses -
+ func tail(in) = out works, also pred empty(in) is semidet,
+ typeclass declarations allow type variables in predicates not listed
among the typeclass parameters,
+ instance declarations allow types which are parametrized (inside), if
an additional typeclass constraint upon the type variables is added,
there seem to be obstacles:
(a) The content type (e.g. intNode, stringNode) cannot be given
explicitly in a typeclass declaration, applying functional depencies
does not appease the compiler.
(b) Without a such lore, the compiler seems not to be able to read it
from the instance declaration (which in fact contains it) - moreover it
seems to be more serious than just a 'typecast' issue - wrapping a
typeclass call in an appropriate function lets the compiler finish
successfully, but seems to produce an endless loop at execution:
In the attached example code, this loop already happens when
uncommenting '% _First = first2(Q2),'.
(c) By directly calling first/1 (just deleting the '2' at line 66 and
recompiling), the compiler complains:
fringe.m:054: In clause for predicate `fringe.main/2':
fringe.m:054: error: ambiguous overloading causes type ambiguity.
fringe.m:054: Possible type assignments include:
fringe.m:054: _First: (pred int) or T
(d) At uncommenting 'insert/2' (equivalent to your 'cons/2') already in
the typeclass & instance declarations, the compiler throws the following
miraculous error message:
fringe.m:027: In clause for type class method implementation:
fringe.m:027: in unification of variable `HeadVar__3'
fringe.m:027: and term `q_insert(HeadVar__1, HeadVar__2)':
fringe.m:027: type error in argument(s) of functor `q_insert/2'.
fringe.m:027: Argument 1 (HeadVar__1) has type `(some [T] T)',
fringe.m:027: expected type was `(some [T] T)'.
Does this mean existential types are regarded as per se not unifiable?
------------------------
Under the bottom line, I guess what I am trying to achieve is finding a
'crude way' of having rank two polymorphism with )o+>0.13. I'm afraid I
was too optimistic - but my experience is is that I do not stop
discovering new capabilities of Mercury when going this way - poking and
experimenting. I just didn't want to give up too early - and, the
temptation is considerable for somebody stepping out of the OO world
into Mercury.
Of course, I would be interested in what you would consider to be a good
style solution of this problem at )o+>0.13 - perhaps by other means.
Thank you in advance,
Nick
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurylang.org/archives/users/attachments/20061003/150d89db/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: fringe.m
Type: text/x-objcsrc
Size: 1589 bytes
Desc: not available
URL: <http://lists.mercurylang.org/archives/users/attachments/20061003/150d89db/attachment.bin>
More information about the users
mailing list