stampit-org/stamp-specification

Set instance.prototype.constructor?

Closed this issue · 9 comments

There seems to be at least one interesting use-case for granting access to the stamp from the instance.

Indeed, in Stampit, we did have the notion of creating a "self-aware stamp", which is trivial to achieve with the spec as it's currently agreed to:

const selfAware = init((options, { instance, stamp }) => {
  const proto = Object.getPrototypeOf(instance);
  proto.constructor = stamp;
});

This could be part of the spec... however, it has the same reliability problem as instanceof and Constructor.prototype, since the value is mutable after object instantiation.

The mutability really is not a problem, as user can corrupt anything accessible.

Btw, I think we all agree that use of instanceof is bad practice, yes?

Taking this granted, I suggest again that stamp should do both:

  • obj.constructor === stamp
  • obj instanceof stamp

For very simple reason: because this is obviously correct thing to do.

Core JS is all about prototypal OO, not classic OO. And stamps are about protoypal OO. Stamp factory function is constructor, and object is instance of stamp. Yes, they perhaps don't match classic OO that everybody has been trying to do in JS, but so what?

Why disable standard core JS tools just to enforce coding style?

The mutability really is not a problem, as user can corrupt anything accessible.

LOL -- mutability is no problem because the problem is everywhere! =)

In practice, people tend to rely on features like this to get pseudo type-checks at runtime, so yeah, it is a problem, because that doesn't work as expected.

Why disable standard core JS tools just to enforce coding style?

Because those particular tools are exactly the opposite of the good compositional style that Stamps exist to promote in the first place.

The mutability really is not a problem, as user can corrupt anything accessible.

LOL -- mutability is no problem because the problem is everywhere! =)

The problem is not misguided/evil users intentionally changing reserved properties like .constructor or .compose or changing stuff under stamp descriptor directly. It's users' right to blow things up.

The problem is well-intentioned users creating well-designed code and then it will blow up because low-level specs/implementations did not care to prepare for all interactions that objects can have. That's why I'm worried about deep-vs-shallow interactons, and that's why the happy-go-lucky attitude around existing instance modification depresses me.

In practice, people tend to rely on features like this to get pseudo type-checks at runtime, so yeah, it is a problem, because that doesn't work as expected.

Why disable standard core JS tools just to enforce coding style?

Because those particular tools are exactly the opposite of the good compositional style that Stamps exist to promote in the first place.

The style issue does not matter. Really. JS as platform gives us some tools for object creation and tagging and those should be used. As platform standard. Whether and how and which style users. later user does not matter here.

The problem is not misguided/evil users intentionally changing reserved properties like .constructor or .compose or changing stuff under stamp descriptor directly. It's users' right to blow things up.

Agreed.

The problem is well-intentioned users creating well-designed code and then it will blow up because low-level specs/implementations did not care to prepare for all interactions that objects can have.

And this is where the needle slides off the record. Well designed code doesn't type check with instanceof or instance.prototype.constructor. Just because something is in the specs doesn't mean it's good. (e.g. class).

I'm worried about deep-vs-shallow interactons, and that's why the happy-go-lucky attitude around existing instance modification depresses me.

Allowing instance modification opens up the possibility for users to extend things like functions or arrays to create object types other than {}. We can allow those perfectly valid use without cluttering the standard API. Frankly, if a user is doing something like that, I think it's OK to trust that they know what they're doing. If they're using instanceof, you can probably assume that they don't know what they're doing.

A good API makes it easy to do the things that make sense, and hard to do the things that are bad for you. The pit of success vs the pit of despair.

The style issue does not matter.

The whole point of stamps is to promote compositional style. I don't think it makes any sense at all to enable class-oriented tools for use with stamps.

If you want to enable naive type checking, you can at the stamp level:

const selfAwareWithInstanceof = compose({
  initializers: [(options, { stamp }) => {
    stamp.prototype = stamp.compose.methods;
    stamp.prototype.constructor = stamp;
  }]
});

const inst = selfAwareWithInstanceof();
inst instanceof selfAwareWithInstanceof; // true
inst.constructor; // [Function: Stamp]

Is that good enough?

This is even easier with the init() utility that will be included with Stampit.

  • obj.constructor === stamp
  • obj instanceof stamp

I wanted to say that this can be a stamp (composable behaviour) too. But Eric was faster than me. :)

Btw, we already have it in stampit v2 Advanced Examples: https://github.com/stampit-org/stampit/blob/master/docs/advanced_examples.md#self-aware-objects---instancegetstamp-2-ways

While the question comes up fairly frequently from those who are transitioning from class-based to prototype-based OO, ditching instanceof has been beneficial to the community, rather than detrimental. I think that's enough to make this behavior opt-in. We can throw up a self-aware stamp on npm to make it really easy for people who want to add it to their stamps.

Yeap. I have a list of utility stamps to implement. This can be just one of them. :)