User:Colby Russell/Mixins and multiple inheritance

From triplescripts.org wiki

Modular Smalltalk was conceived with multiple inheritance in mind, and I've logged somewhere on the wiki already the disparity, therefore, between Modular Smalltalk and triple scripts. However, because we retain some dynamism where Modular Smalltalk tries to avoid it, authors can still approximate it. Can we make it easier by providing first-class support? I'm not intimately familiar with Ruby, but I know it has something like what follows.

Consider some trait that allows us to send a "foo" message to a class, i.e., the class implements a method foo. We want our class Bar to support this trait, and more specifically, we want to piggyback off foo implemented elsewhere. In our example, the implementor is an existing class FooTrait. In precompilation form, we might allow:

   import { FooTrait } from "./FooTrait.xxx"
   
   export
   class Bar {
       constructor() { /* ... */ }
   }
   
   FooTrait.foo extends Bar

In post-compilation form, we get:

   /// import { FooTrait } from "./FooTrait.xxx"
   
   /// export
   class Bar {
       constructor() { /* ... */ }
   }
   
   Bar.prototype.foo = FooTrait.foo;

This is dependent upon the eventual break with TC39 (once again, not that big of a deal, since after all This is not JS).

You might ask why we don't sidestep the entire schism, though, by simply not introducing special syntax in the first place, i.e., just prescribe the static assignment form. Well, we get richer semantics. Note, also, that we can't just transform into the post-compilation form above, because it's essentially un-roundtrippable. And then there's the issue of getting this to resolve correctly when a Bar instance has its foo called. So instead, we'd really want our post-compilation to look something like:

   /// import { FooTrait } from "./FooTrait.xxx"
   
   /// export
   class Bar {
       constructor() { /* ... */ }
   }
   
   void(`extends`, Bar.prototype.foo = FooTrait.prototype.foo);;

(Note that even if we scuttle this idea, there may be something salvageable from that void-double-colon form that we speculatively introduce above.)

I'm not against, however, something like:

   /// import { FooTrait } from "./FooTrait.xxx"
   
   /// export
   class Bar {
       constructor() { /* ... */ }
   }
   
   $extends$: Bar.prototype.foo = FooTrait.prototype.foo;

... or when mixing in multiple:

   /// import { FooTrait } from "./FooTrait.xxx"
   
   /// export
   class Bar {
       constructor() { /* ... */ }
   }
   
   $extends$: Bar.prototype.foo = FooTrait.prototype.foo;
              Bar.prototype.fum = FooTrait.prototype.fum;

Whatever the case, if we do something like this, we have ordering constraints (just like with "proper" subclassing) since class forces in-order, lexical bindings on us (unlike function declarations, which get hoisted).

Alternatively, since we're optimizing for ergonomics, the prescribed pre-compilation form to mix in multiple slots (without having to repeat the FooTrait. prefix for every one) might have a shorthand that instead looks like:

   import { FooTrait } from "./FooTrait.xxx"
   
   export
   class Bar {
       constructor() { /* ... */ }
   }
   
   +fee (from FooTrait)
   +fi
   +fo
   +fum

(Does that make it start to feel too much like Objective-C/-J? We need to be mindful of trying to maintain offests line-for-line, which constrains how much latitude we have... Plus it needs to be easy to parse.)

Cookies help us deliver our services. By using our services, you agree to our use of cookies.