JavaScript Talents

Composable Units Of Reuse via Delegation Proxies

aka Roles, Mixins, Traits

Flight Mixins ... Anyone?


var Enumerable_first = (function trait () {
  return (function applicator () {
    this.first = function first () {
      return this[0];
    };
  });
}());

var Enumerable_last = (function trait () {
  function last () {
    return this[this.length - 1];
  }
  return (function applicator () {
    this.last = last;
  });
}());

Flight Mixins ... Anyone?


var bazList = ["foo", "bar", "baz"];
var dogList = ["the", "lazy", "dog"];

// - `first` always gets assigned its very own method.

Enumerable_first.call(bazList);
Enumerable_first.call(dogList);

// - `last` also reserves its own slot, but
//   will hold a reference into the closure.

Enumerable_last.call(bazList);  
Enumerable_last.call(dogList);

console.log(bazList.first === dogList.first); // `false`;
console.log(bazList.last === dogList.last);   // `true`;

How about a nicer syntax ...


trait Enumerable_first {
  applicator() {
    this.first = function () {
      return this[0];
    };
  }
}

trait Enumerable_last (
  function last () {
    return this[this.length - 1];
  }
  applicator() {
    this.last = last;
  }
}

... backed by an ES3 library?


var
  Enumerable_first = Trait.create(function (use, applicator) {
    applicator(function () {
      this.first = function () {
        return this[0];
      };
    });
  }),

  Enumerable_last = Trait.create(function (use, applicator) {
    function last () {
      return this[this.length - 1];
    }
    applicator(function () {
      this.last = last;
    });
  });
  • "Trait Composition Language"

    • Composable behavior ... "Traits from Traits"

    • Conflict resolution of equally named functionality

... hhmh, and where does the code-reuse come from?

Apply rules


trait CompositeTrait {        // trait declaration.

  use { Trait_A, Mixin_B, Applicable_C }

  //apply { Mixin_B }
    apply { Applicable_C, Trait_A }
}

var CompositeTrait = trait {  // trait expression.

  use { Mixin_B, Applicable_C }

    apply { Mixin_B:sanitize }
    apply { Applicable_C:rerender }
}
use {..} apply {..}


trait MinimumObservable {                 // trait declaration.

  use { Observable_SignalsAndSlots }

    apply all without { hasEventListener, removeEventListener }
}

var NoHasObservable = trait Observable {  // named trait expression.

  use { Observable_SignalsAndSlots }

    apply { Observable_SignalsAndSlots } without { hasEventListener }
}
use{..} apply all
use{..} apply all without {..}
use{..} apply {..} without {..}

Apply rules

trait Talktive_AB {

  use { Talktive_A, Talktive_B }

    apply { Talktive_A:smallTalk }
    apply { Talktive_B:smallTalk } as { talk }
    apply { Talktive_A:bigTalk } before { Talktive_B }
}

var talker = Talktive_AB.call({});

talker.talk();      // console does log       : " bbb "
talker.smallTalk(); // console does log       : " aaa "
talker.bigTalk();   // 1stly console does log : " AAA "
                    // 2ndly console does log : " BBB "
use{..} apply {..} as {..}

Apply rules

use{..} apply {..} after {..}
use{..} apply {..} before {..}

trait withCompositeView {
  use { withView_A, withView_B, withView_C }

    apply { withView_A:register }
      before { withView_B } before { withView_C }

    apply { withView_A:initialize }
      after { withView_B } after { withView_C }

    apply { withView_C:render }
      before { withView_B } after { withView_A }
}

Apply rules

var view = withCompositeView.call({});

view.register();      // behavior's execution order : A-B-C
view.initialize();    // behavior's execution order : C-B-A
view.render();        // behavior's execution order : A-C-B

All of the proposed trait composition functionality and its syntax gets proved by an already existing ES3 compatible library ...

Apply rules

var
  CompositeTrait = Trait.create(function (use, applicator) {

    use(Trait_A, Mixin_B, Applicable_C)

    //.apply(Mixin_B)
      .apply(Applicable_C, Trait_A);
  }),

  CompositeTrait = Trait.create(function (use, applicator) {

    use(Mixin_B, Applicable_C)

      .apply(Mixin_B, "sanitize")
      .apply(Applicable_C, "rerender");
  });
use(..).apply(..)

var
  MinimumObservable = Trait.create(function Observable () {

    use(Observable_SignalsAndSlots)

      .apply.all.without("hasEventListener", "removeEventListener");
  }),

  NoHasObservable = Trait.create(function Observable () {

    use(Observable_SignalsAndSlots)

      .apply(Observable_SignalsAndSlots).without("hasEventListener");
  });
use(..).apply.all
use(..).apply.all.without(..)
use(..).apply(..).without(..)

Apply rules

var Talktive_AB = Trait.create(function (use, applicator) {

  use(Talktive_A, Talktive_B)

    .apply(Talktive_A, "smallTalk")
    .apply(Talktive_B, "smallTalk").as("talk")
    .apply(Talktive_A, "bigTalk").before(Talktive_B);
});

var talker = Talktive_AB.call({});

talker.talk();      // console does log       : " bbb "
talker.smallTalk(); // console does log       : " aaa "
talker.bigTalk();   // 1stly console does log : " AAA "
                    // 2ndly console does log : " BBB "
use(..).apply(..).as(..)

Apply rules

use(..).apply(..).after(..)
use(..).apply(..).before(..)

... and some trait composition examples on JSFiddle.

... got it, but what is it with this   applicator     again? And how, in the end, do I compose objects/types?

applicator
trait withFirstLastEnumerable {
  function first() { return this[0]; }
  function last() { return this[this.length - 1]; }

  applicator() {
    this.first = first;
    this.last = last;
  }
}
trait withEnumerableListProxy {
  applicator(list) {
    withFirstLastEnumerable.call(list);

    this.first = function () { return list.first(); };
    this.last = function () { return list.last(); };
  }
}

applicator ‧ call / apply

local functions, trait composition and stateful traits

class EnumQueue {
  constructor() {
    var
      list  = [],
      queue = this;

    queue.enqueue = function(item) {
      return (list.push(item) && item);
    };
    queue.dequeue = function() {
      return (list.shift());
    };
    withEnumerableListProxy.call(queue, list);
  }
}

applicator ‧ call / apply

local functions, trait composition and stateful traits

Trait Manual / Cheat Sheet

Library Example

var
  withFirstLastEnumerable = Trait.create(function (use, applicator) {
    function first() { return this[0]; }
    function last() { return this[this.length - 1]; }

    applicator(function () {
      this.first = first;
      this.last = last;
    });
  }),
  withEnumerableListProxy = Trait.create(function (use, applicator) {
    applicator(function (list) {
      withFirstLastEnumerable.call(list);

      this.first = function () { return list.first(); };
      this.last = function () { return list.last(); };
    });
  });
applicator ‧ call / apply

local functions, trait composition and stateful traits

JavaScript Talents

Composable Units Of Reuse via Delegation Proxies

aka Roles, Mixins, Traits

Thank You.

Made with Slides.com