Object.mixin() reacher proposal

Peter Seliger peter.seliger at googlemail.com
Sun Apr 14 20:56:50 PDT 2013


Hi, maybe one should discuss terminology first.

What has been rediscovered within the last decade every now and again by
JavaScript programming individuals, this languages capability for
"functional or function based mixins", might deserve a closer look, before
running into what I would call a meta programming temptation trap.

We already have everything we need in order to create modular collections
of behavior and also in order to provide and apply them to objects.

"Mixin" might be a Ruby influenced term and does not completely cover what
can be achieved by functions/closures [call] and [apply]. I'd rather tend
to use Perl 6 "Roles" as a generic term.

The smallest thinkable Role was a function body that implements a single
method in a stateless way. Thus being a "Trait" if one follows the findings
of the "Software Composition Group" at Bern University [
http://scg.unibe.ch/research/traits]. Any implementation that gets injected
mutable state or does create mutable state on its own in oder to solve its
task(s) then, from my point of view, should be referred to as "Mixin".

> It seems that Mixins Are Awesome and this can take most advantages from
being a function and not only an object:
> http://webreflection.blogspot.ie/2013/04/flight-mixins-are-awesome.html
>
> AFAIK, all interfaces described in W3C such EventTarget and others could
be also covered by this proposal ... so ... what do you think ?

Andrea, you are right, but all it needs in my opinion are a module pattern,
a module library of your choice, a naming convention for your
Trait/Mixin-module Implementation (adjectives/adverbes, no nouns, first
uppercase letter?) and again [call] or [apply].

For your given example of W3C interfaces, [EventTarget] should be
implemented and internally referred to as [EventTargetMixin], but the
module should expose [EventTargetMixin] as [Observable].

- example gist for Observable [https://gist.github.com/petsel/5385218].
- example gist for a Trait [https://gist.github.com/petsel/5385163].

There is another point that makes pure straightforward functional
Trait/Mixin composition unique in JavaScript - passing additional data at
apply time. One could write e.g. implementations that at apply time can
adapt the naming of their functional API without changing the underlying
implementation itself. [Observable] from the above provided  gist example
enables configuration of the default API method names by an optionally
passed config object making it
possible to serve all those weird observable API dialects.

var obj_01 = {};
var obj_02 = {};

Observable.call(obj_01); /* does add the default api methods
[addEventListener], [removeEventListener], [hasEventListener] and
[dispatchEvent] */

Observable.call(obj_02, {
  addEventListener: "on",
  removeEventListener: "off",
  hasEventListener: "hasObserver",
  dispatchEvent: "trigger"
}); /* obj_02 does feature the custom api names [on], [off], [hasObserver]
and [trigger] - the underlying methods are the same as of obj_01 */

With meta programming approaches this advantage, one gets for free now,
might get lost or if implemented less understandable.

I discarded every single approach I made within the last 5 years for
Trait/Mixin libraries, that tried to be smarter than what the languages
core already provides except the last one that just sticks to a module
library.




On Sun, Apr 14, 2013 at 11:48 PM, Andrea Giammarchi <
andrea.giammarchi at gmail.com> wrote:

> somebody already raised the concern "what if I want to mixin the function
> as object, not as callable"
>
> I think being [[Call]] not possible to mixin as object functionality, and
> being functions all by default having ownProperties such ["arguments",
> "name", "length", "caller"] .. that would simply clash so function as
> argument, for this purpose, is never ambiguous, but of course a function
> could be the target object, if needed
>
> var withMoar = Object.mixin(function(){}, mixinFunction);
> // same as mixinFunction.call(function(){});
>
>
>
>
>
>
>
> On Sun, Apr 14, 2013 at 2:24 PM, Angus Croll <anguscroll at gmail.com> wrote:
>
>> yeah that's better - I was having a senior moment - most constructor
>> functions will normally reside in the prototype of course
>>
>>
>> On Sun, Apr 14, 2013 at 1:59 PM, Andrea Giammarchi <
>> andrea.giammarchi at gmail.com> wrote:
>>
>>> My previous version was doing that in a probably too smart way so I've
>>> simplified the proposal simply accepting, in that example
>>>
>>> Object.mixin(Thung.prototype, Thing.proottype);
>>> Object.mixin(Thung.prototype, Thang.proottype);
>>>
>>> It does not look so black magic anymore but it's way less ambiguous than
>>> the first proposal (I guess)
>>>
>>> Thanks
>>>
>>>
>>>
>>>
>>> On Sun, Apr 14, 2013 at 1:34 PM, Angus Croll <anguscroll at gmail.com>wrote:
>>>
>>>> Lending my support to Object.mixin accepting a function as the
>>>> argument—but no surprise there I guess :)
>>>>
>>>> Note: since functional mixins and constructors are syntactically
>>>> identical we can now get gorgeously expressive—and make type inheritance
>>>> way simpler (for the first time allowing multiple type inheritance)
>>>>
>>>> //make a new thing and a new thang
>>>> var thing = new Thing;
>>>> var thang = new Thang;
>>>>
>>>> //OR have Thung inherit from Thing and Thang
>>>> Object.mixin(Thung.prototype, Thing);
>>>> Object.mixin(Thung.prototype, Thang);
>>>>
>>>>
>>>> On Sun, Apr 14, 2013 at 12:59 PM, Andrea Giammarchi <
>>>> andrea.giammarchi at gmail.com> wrote:
>>>>
>>>>> right, I've simplified a lot and tested cross platform:
>>>>>
>>>>> https://github.com/WebReflection/object-mixin#object-mixin
>>>>>
>>>>> thoughts?
>>>>>
>>>>>
>>>>> On Sun, Apr 14, 2013 at 10:07 AM, Andrea Giammarchi <
>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>
>>>>>> OK, maybe just code was a non-sense ...
>>>>>>
>>>>>> So, the idea behind is mark a function explicitly as mixin ... how ?
>>>>>>
>>>>>> Any function that is passed and has an empty prototype (then is user
>>>>>> defined or native) could be considered invocable as mixin.
>>>>>>
>>>>>> function addFunctionality() {
>>>>>>   this.method = function () {
>>>>>>     // now the outer context has a method
>>>>>>   };
>>>>>> }
>>>>>>
>>>>>> // mark the prototype as empty in ES5
>>>>>> delete addFunctionality.prototype.constructor;
>>>>>>
>>>>>> function MyClass() {}
>>>>>>
>>>>>> Object.mixin(MyClass.prototype, addFunctionality);
>>>>>>
>>>>>> rather than only
>>>>>>
>>>>>> Object.mixin(MyClass.prototype, {method: function () {}});
>>>>>>
>>>>>> If the prototype has at least one own property in its prototype it
>>>>>> will be considered a constructor so that:
>>>>>>
>>>>>> Object.mixin(MyClass.prototype, MySuperClass);
>>>>>>
>>>>>> can easily be transformed implicitly into:
>>>>>> Object.mixin(MyClass.prototype, MySuperClass.prototype);
>>>>>>
>>>>>>
>>>>>> This case is, however, less important, the fact Object.mixin should
>>>>>> be able to accept a function and invoke it with target as context with
>>>>>> optional arguments would be really a **great idea**, IMHO
>>>>>>
>>>>>> Thanks
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Sun, Apr 14, 2013 at 2:45 AM, Andrea Giammarchi <
>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>
>>>>>>> also, in case you are guessing the typo .. reacher because it could
>>>>>>> reach more (older) engines, doing a joke with richer .... got it? .. too
>>>>>>> damn fun, I know!
>>>>>>>
>>>>>>>
>>>>>>> On Sun, Apr 14, 2013 at 2:04 AM, Andrea Giammarchi <
>>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>>
>>>>>>>> apologies
>>>>>>>> getOwnPropertyDescriptor(
>>>>>>>>     source,
>>>>>>>>     key
>>>>>>>> )
>>>>>>>>
>>>>>>>> should have been
>>>>>>>> getOwnPropertyDescriptor(
>>>>>>>>     enricher,
>>>>>>>>     key
>>>>>>>> )
>>>>>>>>
>>>>>>>>
>>>>>>>> On Sun, Apr 14, 2013 at 1:58 AM, Andrea Giammarchi <
>>>>>>>> andrea.giammarchi at gmail.com> wrote:
>>>>>>>>
>>>>>>>>> what I've written here:
>>>>>>>>>
>>>>>>>>> https://github.com/WebReflection/object-mixin/blob/master/src/object-mixin.js
>>>>>>>>>
>>>>>>>>> is a better proposal for the potential `Object.mixin()` in current
>>>>>>>>> ES6 specs.
>>>>>>>>>
>>>>>>>>> It seems that Mixins Are Awesome and this can take most advantages
>>>>>>>>> from being a function and not only an object:
>>>>>>>>>
>>>>>>>>> http://webreflection.blogspot.ie/2013/04/flight-mixins-are-awesome.html
>>>>>>>>>
>>>>>>>>> AFAIK, all interfaces described in W3C such EventTarget and others
>>>>>>>>> could be also covered by this proposal ... so ... what do you think ?
>>>>>>>>>
>>>>>>>>> /*jslint browser: true, forin: true, plusplus: true, indent: 4 */
>>>>>>>>> (function(Object, mixin) {
>>>>>>>>>     "use strict"; // happy linter ^_____^
>>>>>>>>>
>>>>>>>>>     /* <droppable>
>>>>>>>>>      * adhoc polyfill section for this purpose only
>>>>>>>>>      * never use these functions outside this closure ... like ...
>>>>>>>>> ne*/var
>>>>>>>>>     /*
>>>>>>>>> ^ ... you see that? only reason I chose 4 spaces indentations here
>>>>>>>>> :D
>>>>>>>>>       also this comment ... pure quality, right ?!?! ... anyway
>>>>>>>>> ... */
>>>>>>>>>
>>>>>>>>>         // for IE < 9 Desktop browsers
>>>>>>>>>         defineProperty = Object.defineProperty ||
>>>>>>>>>         function (o, k, d) {
>>>>>>>>>             o[k] = d.value;
>>>>>>>>>         },
>>>>>>>>>         // same as above
>>>>>>>>>         getOwnPropertyNames = Object.getOwnPropertyNames ||
>>>>>>>>>         function (o) {
>>>>>>>>>             var
>>>>>>>>>                 // in case the guy does not inherit from
>>>>>>>>> Object.prototype
>>>>>>>>>                 has = Object.prototype.hasOwnProperty,
>>>>>>>>>                 result = [],
>>>>>>>>>                 key;
>>>>>>>>>             for (key in o) {
>>>>>>>>>                 // in non ES5 compliant browsers
>>>>>>>>>                 // there's no way to define properties
>>>>>>>>>                 // as non enumerable unless these are
>>>>>>>>>                 // there by default, like "constructor" is
>>>>>>>>>                 // for functions.prototype
>>>>>>>>>                 if (has.call(o, key)) {
>>>>>>>>>                     result.push(key);
>>>>>>>>>                 }
>>>>>>>>>             }
>>>>>>>>>             return result;
>>>>>>>>>         },
>>>>>>>>>         // again ... IE < 8
>>>>>>>>>         getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
>>>>>>>>> ||
>>>>>>>>>         function (o, k) {
>>>>>>>>>             return {
>>>>>>>>>                 enumerable: true,
>>>>>>>>>                 writable: true,
>>>>>>>>>                 configurable: true,
>>>>>>>>>                 value: o[k]
>>>>>>>>>             };
>>>>>>>>>         };
>>>>>>>>>     // </droppable>
>>>>>>>>>
>>>>>>>>>     // if already defined get out of here
>>>>>>>>>     // this should be
>>>>>>>>>     // if (mixin in Object) return;
>>>>>>>>>     // but for some reason I went for JSLint ...
>>>>>>>>>     if (Object[mixin]) {
>>>>>>>>>         return;
>>>>>>>>>     }
>>>>>>>>>     // same descriptor as other spec'd methods
>>>>>>>>>     defineProperty(
>>>>>>>>>         Object,
>>>>>>>>>         mixin,
>>>>>>>>>         {
>>>>>>>>>             enumerable: false,
>>>>>>>>>             writable: true,
>>>>>>>>>             configurable: true,
>>>>>>>>>             value: function mixin(
>>>>>>>>>                 target, // object to enrich with
>>>>>>>>>                 source    // mixin object/function
>>>>>>>>>             ) {
>>>>>>>>>                 var
>>>>>>>>>                     // check if source is a function
>>>>>>>>>                     enricher = typeof source === 'function' ?
>>>>>>>>> source.prototype : source,
>>>>>>>>>                     // per each own property name
>>>>>>>>>                     keys = getOwnPropertyNames(enricher),
>>>>>>>>>                     length = keys.length,
>>>>>>>>>                     i = 0,
>>>>>>>>>                     key;
>>>>>>>>>                 while (i < length) {
>>>>>>>>>                     // define it ...
>>>>>>>>>                     defineProperty(
>>>>>>>>>                         target,
>>>>>>>>>                         key = keys[i++],
>>>>>>>>>                         // ... via same property descriptor
>>>>>>>>>                         getOwnPropertyDescriptor(
>>>>>>>>>                             source,
>>>>>>>>>                             key
>>>>>>>>>                         )
>>>>>>>>>                     );
>>>>>>>>>                 }
>>>>>>>>>                 // if the object had no own names
>>>>>>>>>                 // it's quite clear the intention of the user
>>>>>>>>>                 // so that if a function without properties
>>>>>>>>>                 // is passed through this method ...
>>>>>>>>>                 if (!length && typeof source === 'function') {
>>>>>>>>>                     // this function is invoked with the target
>>>>>>>>>                     // as its own context
>>>>>>>>>                     source.apply(
>>>>>>>>>                         target,
>>>>>>>>>                         // optional arguments to initialize
>>>>>>>>> defaults
>>>>>>>>>                         // for this mixin might be accepted too
>>>>>>>>>                         keys.slice.call(arguments, 2)
>>>>>>>>>                     );
>>>>>>>>>                 }
>>>>>>>>>                 // always return the initial target
>>>>>>>>>                 // ignoring a possible different return
>>>>>>>>>                 // in latter case: consistency with this method
>>>>>>>>>                 return target;
>>>>>>>>>             }
>>>>>>>>>         }
>>>>>>>>>     );
>>>>>>>>> }(Object, 'mixin'));
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>> _______________________________________________
>>>>> es-discuss mailing list
>>>>> es-discuss at mozilla.org
>>>>> https://mail.mozilla.org/listinfo/es-discuss
>>>>>
>>>>>
>>>>
>>>
>>
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20130415/adcf4c1b/attachment-0001.html>


More information about the es-discuss mailing list