[clean-list] ADTs for fields in record types, please!

Fergus Henderson fjh@cs.mu.OZ.AU
Tue, 14 Oct 2003 12:56:37 +1000


On 14-Oct-2003, Marco Kesseler <m.wittebrood@mailbox.kun.nl> wrote:
> >> I do find the "constructors" more clear in this situation, but the 
> >> "problem" I have with this, is that the "methods" have to be very 
> >> specific in their public signature about what parts of the object 
> >> they refer to. In the end, this affects maintainability of the code, 
> >> as you have to change the method signatures as soon as they start 
> >> taking different object fields as arguments, which should be 
> >> irrelevant for the "external" interface.  This problem also exists in 
> >> solutions that employ type class constraints: the contraints provide 
> >> an interface on an _element_ of the object, not on the object 
> >> _itself_ (or is there a way?).
> >
> >I didn't really understand what you were getting at there.
> >Perhaps an example might help to clarify your point.
> 
> The main problem is that the "Integer" object below exposes too much 
> of its internal types. There should be no reason to tell the outside 
> world that inc works on the "i" field like this.
> 
> :: Integer = E.i: {
>    value	:: i,
>  		inc		:: i -> i,
>  		print	:: i -> String
>  	}

But "i" here is completely abstract.  There's really no exposing of
internal implementation details of `i' going on here.

> So we end up having to define "wrapper" functions to hide this flaw 
> (like the ones you suggested), and this introduces extra work.

The wrappers are indeed a little bit of extra work.  So it would
be nice to be able to use a type-class constrained existential type,
like you can do in Glasgow Haskell and Mercury, e.g. something like
(inventing new Clean syntax, and overloading the name "Integer" as
both a type class and a type):

	:: Integer = E.i | Integer i: { value :: i }

It would even be nice to have such a type generated automatically from
the class declaration, and to have the instance generation that makes
the type be an instance of the class be similarly generated automatically.
(Of course this only works in a restricted set of cases, not for all
type classes, e.g. it only makes sense for single-parameter type
classes, not multi-parameter type classes.)

> Things 
> would be simpler with objects like the following, as they would allow 
> one to specify that they operate on the object they are contained in, 
> without reveiling on what fields exactly. Dealing with these fields 
> would then become purely a matter of the method itself, not of some 
> external wrapper function.
> 
> :: Integer = E.i {
> 		value	:: i,
> 		inc		:: Integer -> Integer,
> 		print	:: Integer -> String
> 	}

Now here I disagree.  Using an approach like that isn't simpler at all, IMHO.
Firstly, like the original work-around, but unlike the solution using
typeclass-constrained existential types, you've duplicated the "inc"
and "print" members from the class in the data type.  
Secondly, it is fundamentally not type safe, because it allows you to
apply methods from one type of Integer to a different type of Integer.

-- 
Fergus Henderson <fjh@cs.mu.oz.au>  |  "I have always known that the pursuit
The University of Melbourne         |  of excellence is a lethal habit"
WWW: <http://www.cs.mu.oz.au/~fjh>  |     -- the last words of T. S. Garp.