[clean-list] ADTs for fields in record types, please!
Fergus Henderson
fjh@cs.mu.OZ.AU
Wed, 15 Oct 2003 12:15:15 +1000
On 15-Oct-2003, Marco Kesseler <m.wittebrood@mailbox.kun.nl> wrote:
> On Tue, 2003-10-14 at 04:56, Fergus Henderson wrote:
> > On 14-Oct-2003, Marco Kesseler <m.wittebrood@mailbox.kun.nl> wrote:
> > >
> > > 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.
>
> Yes, but I was not talking about the internals of 'i'. I was talking
> about the internals of 'Integer'. If you add some additional field to
> the Integer object, one would have to "expose" the (possibly concrete)
> type of this field as well in the methods that use it. And one would
> have to adapt the wrapper functions.
But, other than adding additional higher-order function terms corresponding
to additional methods, you *won't* change the Integer object.
> :: Integer = E.i {
> value :: i,
> numberOfIncCalls :: Int,
> inc :: Int i -> (Int, i)
> print :: i -> String
> }
>
> Now you could argue that the complete state of each object should be
> existential, and that any new fields should be added IN this type (i.e.
> within the definition of i).
Exactly!
> If that is what you are proposing, you may
> be right: little seems lost if people know that the methods act on this
> type. And indeed: this would really hide ALL fields.
Right.
> But then suppose that the "print" function suddenly needs to call "inc"
> internally. Must we then change the type of the print function
No... why would you need to do that?
print just calls inc.
> > 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.)
>
> I don't quite follow this.
I'm just saying that if you have a type class declaration such as
-- Haskell syntax
typeclass Foo a where
foo a -> Int
bar a -> a -> a
...
then it would be nice if the compiler would automatically generate
a corresponding existential type AnyFoo
-- (Glasgow) Haskell syntax
data AnyFoo = forall a . Foo a => MkAnyFoo a
and a corresponding instance declaration
-- Haskell syntax
instance Foo AnyFoo where
foo (MkAnyFoo f) = foo f
bar (MkAnyFoo x) (MkAnyFoo y) = bar x y
...
Though probably this would work best if it didn't actually add the "Any"
and "MkAny" prefixes that I used above, and instead just overloaded the
name "Foo" as three things: a type class name, a type name, and a data
constructor name.
My parenthetical remark above was just saying that if you have a type
class declaration for a multi-parameter type class, e.g.
typeclass Quux a b where
...
then you don't want the compiler to automatically generate a
corresponding existential type and instance declaration, since
they wouldn't make sense.
> If an object has more than one existential
> type, how do you then define an interface for it using a type-class
> constraint?
I'm not sure what you mean by "more than one existential type".
If you are talking about an object that can satisfy more than one interface,
then you can define a type class for each interface.
> > > 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.
>
> Ehm, I suspect that I didn't express myself clearly enough. The
> (invented!) notation above would actually BE the class definition. I
> should have made clear that I was not talking about any real Clean
> syntax here (the compiler won't accept the construct above).
The code above looks like valid Clean syntax to me, with an already-defined
meaning. Oh, apart from the fact that it is missing a ":" after the "E.i" --
is that what you meant? I thought that was just a typo.
If you are proposing that the presence or absence of a ":" here have some
semantic meaning, then I think that is a rather confusing and error-prone
choice of syntax.
If you are proposing a new meaning for what is already valid Clean syntax,
then that is definitely confusing and IMHO is not a good idea.
--
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.