1
0
Fork 0
Commit Graph

3 Commits (e93f3a70f9a699f1577df6ce3ba9201b2ea97542)

Author SHA1 Message Date
Mike Gerwitz 903a1a135c
Prevent mixin failure on null/undefined supertype properties 2015-10-26 22:17:28 -04:00
Mike Gerwitz d9b86c1544
Support for trait class supertype overrides
Traits can now override methods of their class supertypes.  Previously, in
order to override a method of some class `C` by mixing in some trait `T`,
both had to implement a common interface.  This had two notable downsides:

  1. A trait that was only compatible with details of `C` could only work
     with `C#M` if it implemented an interface `I` that declared `I#M`.
     This required the author of `C` to create interfaces where they would
     otherwise not be necessary.

  2. As a corollary of #1---since methods of interfaces must be public, it
     was not possible for `T` to override any protected method of `C`; this
     meant that `C` would have to declare such methods public, which may
     break encapsulation or expose unnecessary concerns to the outside
     world.

Until documentation is available---hopefully in the near future---the test
cases provide detailed documentation of the behavior.  Stackable traits work
as you would expect:

```javascript
var C = Class(
{
    'virtual foo': function()
    {
        return 'C';
    },
} );

var T1 = Trait.extend( C,
{
    'virtual abstract override foo': function()
    {
        return 'T1' + this.__super();
    },
} );

var T2 = Trait.extend( C,
{
    'virtual abstract override foo': function()
    {
        return 'T2' + this.__super();
    },
} );

C.use( T1 )
    .use( T1 )
    .use( T2 )
    .use( T2 )
    .foo();
// result: "T2T2T1T1C"
```

If the `override` keyword is used without `abstract`, then the super method
is statically bound to the supertype, rather than being resolved at runtime:

```javascript
var C = Class(
{
    'virtual foo': function()
    {
        return 'C';
    },
} );

var T1 = Trait.extend( C,
{
    'virtual abstract override foo': function()
    {
        return 'T1' + this.__super();
    },
} );

var T2 = Trait.extend( C,
{
    // static override
    'virtual override foo': function()
    {
        return 'T2' + this.__super();
    },
} );

C.use( T1 )
    .use( T1 )
    .use( T2 )
    .use( T2 )
    .foo();
// result: "T2C"
```

This latter form should be discouraged in most circumstances (as it prevents
stackable traits), but the behavior is consistent with the rest of the
system.

Happy hacking.
2015-10-24 23:53:23 -04:00
Mike Gerwitz 867127ed2f Trait class extension support
"Extending" a class C simply creates a contract stating that the trait may
only be mixed into something of type C (so, C or its subtypes).
2015-05-27 23:23:47 -04:00