stampit-org / stampit

OOP is better with stamps: Composable object factories.
https://stampit.js.org
MIT License
3.02k stars 102 forks source link

Functional mixins #146

Closed koresar closed 8 years ago

koresar commented 9 years ago

There is a 4 years old term: functional mixin

The idea behind it is very similar to stampit's. - "has-a" or "uses-a" relationships between composed components.

Correct me where I'm wrong.

Differences to stampit I found so far (please, add up):
var RoundButton = function(radius, label, action) {
    this.radius = radius;
    this.label = label;
    this.action = action;
};

asButton.call(RoundButton.prototype);
asCircle.call(RoundButton.prototype);

var button1 = new RoundButton(4, 'yes!', function() {return 'you said yes!'});
button1.fire(); //'you said yes!'
ericelliott commented 9 years ago

You can have only one initializator (aka constructor). In stampit you can have as many as you want.

  • [x] Additional point: You should never export constructors from modules because they couple all call sites to the constructor implementation.

The benchmarks are fast. Object creation is optimized because of using the new keyword and omitting _.extend for each object created.

koresar commented 9 years ago

We can reference those functional mixins in stamp wikipedia page. Huh?

ericelliott commented 9 years ago

I don't think we need to address functional mixins in our docs, and the wikipedia page has been deleted. Apparently we need to get more people writing about stamps so there are more sources than just my book. ;)

Can we close this issue?

koresar commented 9 years ago

Functional mixins got continuation. With ES7 decorators it looks very much alike composables but with a different syntax. Read here: http://raganwald.com/2015/06/26/decorators-in-es7.html The composition syntax looks like this:

@BookCollector @Author
class BookLover extends Person {};

Where BookCollector, Author are composable behaviors (mixins) and the Person is ES6 class.

I see several downsides of this approach. Anyone else see them?

acao commented 8 years ago

Ok so I'm new to ESNext and very very new to the stamps concept, but I can't seem to find how any of this can't be accomplished without decorators (and maybe also Reflection and Proxies which I know very little about), save for being able to instantiate a stamp without using new. So to answer your question, I don't think I can see any downsides immediately but I'm probably overlooking something.

https://github.com/wycats/javascript-decorators has some examples showing how to decorate classes not only in the way your example gives above, but with arguments, and also to decorate methods and properties by using decorators inside a class. This repo is the spec used by Babel's 'experimental' (used widely) implementation of decorators.

I'll grab a few examples from there for reference:

@isTestable(true)
class MyClass { }

function isTestable(value) {
   return function decorator(target) {
      target.isTestable = value;
   }
}

Passing an argument - factories here we come!

class C {
  @enumerable(false)
  method() { }
}

function enumerable(value) {
  return function (target, key, descriptor) {
     descriptor.enumerable = value;
     return descriptor;
  }
}

So it seems that you can use decorators as factories and also to enumerate classes. This allows you to avoid class inheritance with an incredible degree of flexibility, and leverage the power of javascript's prototypical inheritance powers very similarly to what stamps achieves.

Also, because decorators use Object.defineProperty in a way that precedes your defined class, you could use this to backfill and overwrite class properties in your constructor() method. For example:

@annotation
class MyClass {
  // override the decorator-provided property at the constructor() level so your class can do things with it after instantiation
   constructor(){
     this.isMyClass = true;
   }
   // or you could even override a property at the getter level!
   get annotated(){
     return `we don't need no annotation`;
   }
 }

function annotation(target) {
   // Add properties to target
   target.annotated = true;
   target.isMyClass = false;
}

Lastly, the descriptor argument in a decorator function has some niceties.

let descriptor = {
  value: specifiedFunction,
  enumerable: false,
  configurable: true,
  writable: true
};

(To be honest I don't totally understand this yet but the spec has some nice examples)

And one last thing - because stamps are a specification, it is quite possible that a set of convenience decorators/etc could be used to create stamps!

I wonder if someone could create a minimalist ESNext implementation of the stamp concept with all the desired bells and whistles, using as much of the es6/7/etc spec that is available to, say, Babel, as is possible.

Once 'desugared' it would probably look similar to this library!

Looking forward to someone proving me wrong, because I've only scratched the surface with all of this yet!

koresar commented 8 years ago

Let me start with a quote:

social networks rank strategies prioritize the popular and habitual

I would rephrase to:

people prefer the popular and habitual

Classes are habitual. Decorators are popular in C# and Java (and few other languages). It's logical to see people (re)implementing the same in all other new languages like JavaScript.

The downsides listed by Eric above are pretty much applicable to the decorators too.

It is a very, very hard choice. Trust me. I did that for years in C#.

Also, stampit deliberately avoid class, constructor, new and instanceof keywords applications to stamps. These are antipatterns in JS (see Eric's link above).

Additionally, while applying decorators you need to know which object they are applicable to: class, method, data, etc. Whereas in stampit you just .compose().

The JavaScript property descriptors (aka Object.defineProperty) with all the bells and whistles are coming to stampit v3. And they are already implemented and described here: https://github.com/stampit-org/stamp-specification

Saying all that I am thinking of using decorators and stamps together. They can be a good match!