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

Marco Kesseler m.wittebrood@mailbox.kun.nl
Thu, 16 Oct 2003 00:24:25 +0200


On Wed, 2003-10-15 at 04:15, Fergus Henderson wrote:
> On 15-Oct-2003, Marco Kesseler <m.wittebrood@mailbox.kun.nl> wrote:
> > 
> > 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.

I was rather thinking about changing existing type signatures
corresponding to existing methods that need to be changed because at
some point in time they happen to need access to some additional field.

But yes, this holds for new methods too.

There may be many of them, and it should not be necessary.

> > 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.

Well, IF inc is known at compile-time, then yes. But inc is just a field
in this particular object. It can be replaced by another value, even if
it acts on an existential value: there may be a method in the object
that arranges this.

I think that there can be good reasons to change the behaviour of some
existing object at runtime. Building a new object may not be an option,
because you may not be able to access the internals of the existing one.

But also because functions are values, so there should be no fundamental
difference between fields and methods.

> >
> 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".

[note that the following is NOT Clean syntax. More info can be found
entirely below.]

class Window c = E.m b g t e: {
  content       :: c,
  menus         :: m,             // some menu definition
  buttons       :: b,             // some definition of buttons
  theme         :: t,             // some window theme
  gizmos        :: g,             // some set of gizmo's
  environment   :: e,             // some relation to some environment
  position      :: Pos,
  size          :: Size,
  move          :: Pos -> Window,
  resize        :: Size -> Window,
  addContent    :: c -> Window
}

I may prefer the above over [This IS Clean syntax]:

:: Window c = E.s: {
  windowState   :: s,
  move          :: s Pos -> s,
  resize        :: s Size -> s,
  addContent    :: s c -> s
}

because in the latter case, I have to look somewhere else to see what a
window is about. Or for whatever reason. I think that it is quite
natural to have multiple existential types in some object class.

Note that I am NOT really considering the use of existential types for
data hiding. Most of all, I find them useful for defining extensible
types. I think that data hiding deserves its own constructs, such as
just exposing the "public" stuff in the Clean dcl files.

> If you are talking about an object that can satisfy more than one interface,
> then you can define a type class for each interface.

No, I did not have that in mind. See above.

Yes, one should be able make any of the the Windows classes above an
instance of various type classes and thus give it various interfaces.

I also think that one should be able to place type-class constraints on
one or more of their existential fields. This would allow one to select
one of these existential fields and actually do something with it (i.e.
without having to define additional functions).

(and that is also potentially a reason to have multiple existential
types in the object).

> > 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.

Like I said, this was not intended as real syntax at all, and I should
have made this clear.

Now what I would LIKE is:

* use the term "interface" for what is currrently known as classes in
Clean. Simply because Clean (or Haskell) classes do _not_ capture the
notion of an object state. They just define interfaces for types.

* Repurpose the term "class" for the kind of objects I was talking about
above, and get rid of explicit references to the object itself in method
definitions. So:

// This is NO Clean syntax at all...

class Integer = E.i: {
  value :: i,
  inc   :: -> Integer,
  print :: -> String
}

regards,
Marco