-
Notifications
You must be signed in to change notification settings - Fork 103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Static collisions & modularization #149
Comments
The possible var GuardStatics = stampit().static({ static: function(staticProps) {
return this.static(_.omit(staticProps, ['init', 'props', 'refs', 'methods', 'compose', 'create']));
}); |
👍 Added to description. =) |
@troutowicz @unstoppablecarl @JosephClay Pretty big breaking change API discussion here. Definitely want more eyes on this. |
Still thinking about it… Eric's solution sounds really better to me because it puts all stampit methods into a separate namespace. So there is no risks, if a stamp is handed to someone else, that he will gets confused. Because, as I see it, we can't expect one .init() method to be "more important" than the other (to re-use #148 issue). |
I'm seeing a pretty large growing list of cons for the workaround... Am I leaving anything out of either pros/cons argument? |
Remove this line please. :) This was a pseudo code. Not a real code. The real code will have no dependencies. |
The entire stampit philosophy is not about functional programming. It's about objects which have both state and methods. Please, remove this statement as irrelevant. |
I was thinking to guard stamp's methods inside the stampit itself, so this problem would never arise. However, that's the way @troutowicz implemented statics. |
That's not a workaround which @mdhooge was talking about. He's using |
So end-users don't have to know about GuardStatics? How will that work, exactly? |
That statement is not related to the |
Workaround = work you have to do to route around a problem. vs modularization: problem disappears completely, so you don't have to write any special code to handle it. Noteworthy, the latter also has perf benefits, because the extra GuardStatics code does not have to be interpreted or invoked, ever. |
Maybe it's already time to introduce stampit v3 ;-) As I see it, it is still possible to inject the extracted methods into a stamp. So the existing code base is still valid. |
:))) |
I don't know the purpose of EventEmitter.init() method. But how can we be sure that it will still operate properly if we put stampit method instead? Maybe the only problem is |
Well, yes, I agree you should flip the 2nd and 3rd argument, but then you've just shifted the problem from one side of the equation to the other. It's still a problem. Statics still collide -- that just changes what happens when they do... |
A necessary evil. Constructors suck, but there are lots of constructors because there are lots of people who don't know that constructors suck. ;) |
compose(
props({body: {...}),
methods({...}),
init(() => {...}),
}).prevalidate({put: {body: joi.required()}}); On top you are missing 'Prevalidable` stamp to be composed with. |
This all comes to #80. We can have |
It would probably be Actually, maybe even |
No, it is not. I know stampit internals. :) It'd be same. The main performance bottlenecks are various mixin and forEach functions.
No it is not by the same reason. Please, remove both. Thanks. |
It is if the user has to add code to work around static collision. We either override one or the other, and to avoid that override, the user will need to make a reassignment, and that's at minimum an additional line of code, and at worst a mapping reassign over several props. And that happens every time the stamp in question is composed with other stamps. Worst case scenario, 6 reassignments * n compositions. |
I'd rather be lodash like. import _ from 'lodash'; // <- imports entire module
import {map, forEach, pick, filter} from 'lodash'; // <- imports few methods only stampit: import stampit from 'stampit'; // <- stampit v2 compatible
import {compose, init, props, methods} from 'stampit'; // <- the new functional way, v2 incompatible How about that? |
Maybe even have something like "babel/register" but: require('stampit/register');
Object.compose;
Object.init;
Object.props;
Object.methods;
// or maybe even as global variables :)))
compose;
init;
props;
methods; |
This talk is coming to class-like syntax support. (Eric, this is a pseudo code, not something well-though and tested.) stamp StampName {
init() {} // .init
method1() {} // .methods
static method2() {} // .static
get reference1() {} // .refs
set reference1() {} // .refs
property1: { deep: 'value' } // .props
}
stamp ComposedStamp composes StampName, SomeOtherStamp; // like "extends" in classes |
@ericelliott would you elaborate on that? How more modular stampit can be? (I'm really curious.) |
import stampit from 'stampit'; // <- stampit v2 compatible
import {compose, init, props, methods} from 'stampit'; // <- the new functional way, v2 incompatible I'd add import { compose, init, props, methods, stamp } from 'stampit'; stamp composedStamp from StampName, SomeOtherStamp; // `from` instead of `composes`? |
👍 😎 |
No, because the user decides the names. ;) |
One minor nitpick: Any stamp that uses the v2 compatible will infect any composed stamps with collision danger. We should mention in the doc that users should prefer the modular version if they're making a stamp that will likely be used as a general-purpose mixin. |
Action Items
@koresar Do you want to implement? |
I like the new hotness. 👍 Better to fix the problem (static collision) then to implement workarounds. I don't really consider the import statement trade-off a big deal. If the developer wants to type less: import * as stampit from 'stampit';
const validatedStamp = stampit.compose(
stampit.props({body: { ... }),
stampit.methods({ ... }),
stampit.init(() => { ... }),
Validator
}).prevalidate({ put: { body: joi.required() } }); That does not look pretty when using lots of shortcut methods but may be fine in situations where only one or two shortcut methods are used. To be honest, with this new API, @koresar's production example is better off being rewritten: import * as stampit from 'stampit';
const validatedStamp = stampit.compose(
stampit.stamp({
init() { ... },
props: { body: { ... } },
methods: { ... }
}),
Validator
).prevalidate({ put: { body: joi.required() } }); This would perform faster and is cleaner. |
Sorry guys, just getting to this now. IRL, I use Any terms of what is "faster" should be ignored as a pro. Object construction isn't/shouldn't be a pain point in an application and the current version performs well. I'm fine with the workaround as long as there's still the option to use v2 as is. Modularization to the point of importing 5+ functions works, but puts the burden of setting up the libs' surface area on the user every time. To me, it feels like an extreme and so is still a workaround. Also, var stampit = window.stampit; // do not use!
var compose = stampit.compose;
var init = stampit.init;
var props = stampit.props;
var methods = stampit.methods;
var stamp = stampit.stamp; |
@troutowicz I'd argue that the example is not cleaner. I would rather import a import validator from './validator';
const validatedStamp = validator
.init(function() { ... })
.props({ body: { ... } })
.methods({ ... })
.prevalidate({ put: { body: joi.required() } }); |
That's fine, I'm not arguing against the usefulness of stampit's current immutable shortcut methods. This issue is about static collisions, your example does the exact same thing as @koresar's and does nothing to prevent static collisions. Issue #148 is why this issue was created. It shows, despite how useful they are, why stampit's statics can cause unexpected problems. |
I'm not arguing that the fix won't work. It just feels like a hefty solution to a problem I don't see occurring often. If we implement a warning when passing a colliding static and not overwrite // pseudo code
import stampit from 'stampit';
import { EventEmitter } from 'EventEmitter';
const EventEmittable = stampit.convertConstructor(EventEmitter)
.init(() => {
EventEmitter.init.call(this);
}); Pros:
Cons:
|
Sample warning logic (not tested) |
@JosephClay We've already been over this, and the solution is the best of both worlds. Updated issue description with "New Hotness" details. |
The discussed functional approach does not solve the static collision of the |
We could rename it to Of course, we'll need to deprecate |
The proposed changes are too radical. Every bit of the new module will be incompatible with both v1 and v2. We should consider a separate package for that. "stampit-functional" or alike. |
That's why we'll use a deprecation process. Look at it as a long-term journey. We can deprecate now, offer old-stamp deprecation console warnings for a full year (maybe two), and then release a Breaking change to remove support for AFAIK, changing the name of Toward an open-standard for StampsEventually, I would like stamps to be an open standard implemented by potentially many libraries, not just stampit. Adoption will suffer if users can't use the properties they want to use. |
We can't have an open standard that not even we respect. I'm OK with keeping a chainable version going for people who prefer the legacy API because that is opt-in, but it should be possible to produce stamps that should never collide with userland libraries -- without forcing users to implement a hacky work-around. In other words, both API's should use |
👍 |
I like the idea of Open Stamps Standard. Take a look at promises: https://promisesaplus.com/ _.isFunction(someObject.create); // true
_.isArray(someObject.create.inits); //true (we should rename the "init()" somehow.
_.isObject(someObject.create.refs); //true
_.isObject(someObject.create.props); //true
_.isObject(someObject.create.methods); //true
_.isObject(someObject.create.statics); //true |
Whoah. Mind. Blown. |
@troutowicz @unstoppablecarl @JosephClay Thoughts? |
In the Open Standard the word "stamp" is not going to be understood straight enough. Take promises/futures for example. They are widely known concepts. In the Open Standard we should call our stamps "functional mixins". It will be easier to understand, and also there is a widely known concept. |
I like
What about "composable factories"? |
Also good. Even better I think. :) |
Thinking of Open Standard. Functions are the first class citizens in JavaScript. I was worried all the time that stamps cannot produce functions as object instances. There is always a workaround via
I believe that the MyStamp(); // creates new object
const plainObject = { key: value };
MyStamp(plainObject); // returns the plainObject object
const myFunc = function whatver() {};
MyStamp(myFunc); // returns the myFunc object Thoughts? |
More thinking about Open Standard.
In other words, stamps should flawlessly process any object. No exception, no warning, nothing. The type of the processed object should be up to the user. (Just like promises do.) In the following example I'm using sound processing terminology: const Cutoff = compose(
refs({ min: 5, max: 250 }),
init(function({ instance }) {
return instance < this.min ? this.min :
intance > this.max ? this.max :
instance; })
);
const Aplify = compose(
refs({ factor: 2.5 }),
init(function({ instance }) { return this.factor * instance; })
);
const Amplificator = compose(Aplify, Cutoff);
Amplificator(200); // returns 250 because 200*2.5 is more than 250 cutoff upper limit
Amplificator(20); // returns 50 beacuse 20*2.5 is within the cutoff range 5-250
Amplificator(0); // returns 5 because 0 is less than 0 cutoff lower limit |
We are not changing the name, "Stamp." So it is written. So it shall be.
NOIf you think we have it tough explaining problem now, imagine a world where the name isn't:
If you think it would be easy to reproduce the branding work that has already been done for stamps, you're sorely mistaken. But they're so sparkly and new! ✨ 💖
These aren't names. They're descriptions. I'm happy to discuss alternative descriptions, but we're not changing the name, stamp. Of course people are asking "WTF is a stamp?" but we can't co-opt the already existing term "functional mixins" and try to make it mean something different, and "composable factories" is what stamps are, not what they're called. Nobody knew what a functor was before it was invented. Nobody knew what "JavaScript" was before 2006. ;) Branding starts with a brandable name, and "stamp" is a brandable name. Want people to stop asking what it is? Then we need to make stamps ubiquitous, and we need to make sure the community has a lot of great learning resources. Anybody know somebody who makes amazing explainer videos? ;) |
I thought @koresar WAS talking about an easy to understand description for the specification. Oops. |
Yep. In the gitchat we all agreed that there's no need to change the name. |
This issue was overtaken by the repo. I'm not seeing the original idea implemented. I propose to close this issue. Any objections? |
Close in favor of implementing the spec. |
New Hotness by @koresar
Pros:
Cons:
👍 👏 😎 🎸 🔥 🚒 🍰
Original discussion:
See #148
Our chaining convenience methods are causing real problems for real users trying to build things with stampit. I proposed the solution of modularizing our tools out of the stamp statics:
Pros:
Cons:
@koresar suggests guarding statics by composing another stamp:
Pros:
Cons:
The text was updated successfully, but these errors were encountered: