Skip to content
Daniel Patterson edited this page May 16, 2013 · 1 revision

Proposal:

Add a simple mechanism for mixins to data, that would take a structural object and transform it into another structural object, after which the data brands would be applied.

Example:

fun Eq(self):
  b = brander()
  b.brand(self.{ eq(self, other): b.check(other) })
end

data List deriving Eq:
  | cons(first, rest)
  | empty
end

x1 = cons(1,empty)
x2 = cons(1,empty)
x3 = x1
x1.eq(x2) # returns false
x1.eq(x3) # returns true

Desugaring/Implementation and other concerns:

There is a straightforward desugaring that would make this possible, if we allow a change to brand preservation semantics: brands should be automatically preserved in the case of object extensions that only add fields (ie, no existing fields are modified). The reason why this is necessary is if you included another mixin after Eq, and it extended the object (which it needs to do to really do anything), the brand for Eq would be lost in the current semantics.

This seems safe to do - we give up guaranteeing the absence of fields in an object as an invariant that brands can represent, but that seems like an okay tradeoff. There is still the open question of preserving brands when you do modify fields, but that is a harder problem, and for efficiency we would probably want to not do any deep checks in the case of just adding new fields anyway.

Provided we have that, the desugaring of the above looks roughly like:

data NAME deriving Mx1[, Mx2]*:
  | VARIANT1(FIELD1[, FIELD2]*) with
  e*
   RECORD-LITERAL
[ | VARIANT2]*
end

Causes the following constructor to be created:

fun VARIANT1(VAL1[, VAL2]*):
  NAME-brand.brand(VARIANT1-brand.brand(
    Mx2(Mx1({FIELD1: VAL1[, FIELD2: VAL2]}.{RECORD-LITERAL}))))
end

Which, in English, means that the structural object is created, with its methods, and then the mixed-in functions are applied, in sequence, and the final result is branded with the data and variant brands. One tangential part of this proposal is to make the 'with' block in data be a block, not the contents of a record literal. Right now there is no way of storing private data within a data variant, because there is no way to close over a per-instance variable. If we just make what is after 'with' be a block where the last expression is an object literal, we can declare variables and close over them in methods, which seems useful. It adds a set of braces around the methods, but that seems like a relatively lightweight thing. Also, this sort of lines up with how 'provide' works - it is a block that returns a value (which is expected to be an object, thought not strictly necessary).