A Double Fugue in Traits

By: on May 31, 2013

Traits provide a means of composing an object’s functionality out of smaller, simpler parts. Verbs being limited in number, especially good ones, sometimes you want to compose two traits that share names. Today we’re going to look at how to resolve the dispute.

I have a soft spot for abstract algebra. I realised how we can consider a field to be the melding of two separate groups: we can construct the field of reals from the group of integers under + together with the group of integers under *, with a bit of stitching in the form of the distributivity laws. So let’s make a group:

Trait named: #TGroup
    uses: #()
    category: 'Group-Theory'.

TGroup >> * anObject
    "Return the result of applying the group operation to self and anObject."
    self requirement.

TGroup >> inverse
    self requirement.

TGroup >> identity
    ^ self class identity.

TGroup classTrait >> identity
    self requirement.

Now we can compose the two traits. Er, except we’re going to compose TGroup with itself! Name clashes galore! The uses: argument to Trait creation shows a TraitComposition, wherein we show how the two group structures contribute to the field structure. [1]. We thus have

Trait named: #TField
  uses: TGroup @ {#identity->#zero. #* -> #+. #inverse->#negated} +
              TGroup @ {#identity->#one. #inverse->#reciprocal}
    category: 'Group-Theory'

Note that aliasing – #identity -> #zero – doesn’t remove the method from the Trait. This causes a bit of a problem. In the context of a field, we don’t usually talk of “the identity element”, because of course there are two group operations. We instead talk of the additive identity (“zero”) and the multiplicative identity (“one”). We call one operation “sum” or “+”, and the other we call “product” or “*”. Similarly, the two inverse operations are “unary negation” or “negated”, and “reciprocal”.

The only thing I dislike about composing the two TGroups in this way is that aliasing doesn’t remove the conflicting methods: “identity” and “inverse” remain in the public API of TField. But we can fix this, by excluding the ambiguous selectors:

Trait named: #TField
    uses: TGroup @ {#identity->#zero. #* -> #+. #inverse->#negated} - {#identity. #inverse} +
          TGroup @ {#identity->#one. #inverse->#reciprocal} - {#identity. #inverse}
    category: 'Group-Theory'.

TField classTrait
    uses: TGroup classTrait - {#identity} + TGroup classTrait - {#identity}.

We can’t avoid the awkward double-phrasing of the exclusion (-). It would be much nicer if you could remove the methods from the composition:

(TGroup @ {#identity->#zero. #* -> #+. #inverse->#negated} +
    TGroup @ {#identity->#one. #inverse->#reciprocal}) - {#identity. #inverse}

but neither Squeak’s nor Pharo’s implementation of Traits permit this: exclusion removes methods only from the rightmost trait. Whether that’s simply a bug (in the sense of “gosh, no one’s thought of doing that”) or something intrinsic in the semantics, is as yet an open question.

[1] Of course properties like the distributive laws of + and * aren’t defined here. For that you could use something like SqueakCheck.


Post a comment

Your email address will not be published.


You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>