Coder Social home page Coder Social logo

Comments (11)

getify avatar getify commented on August 10, 2024

As I've mentioned in the other discussion threads, then(..) compliance is not something we should be concerned about, because that's already completely covered by the Promises/A+ spec. Let's take out any tests related to then(..) or catch(..). If you feel the Promises/A+ test suite is missing some test, file a bug/PR against it. We just want to augment with stuff not covered there, which is all the ES6 API surface area stuff.


WRT Promise.prototype.then... I have considered that option in the past, and discarded it as unnecessary. Here's why:

  1. The only reason to put something on the .prototype rather than the instance is to support sub-classing, but I've already declared that sub-classing is out of scope for this polyfill (especially since I think that's an incredibly niche thing which won't be used very often).

  2. Putting it on the prototype as you suggest, but having something of the same name shadowing it on the instance basically means the one on the prototype would never get called, as pr.then(..) would always favor the instance one directly. So the prototype.then would be there for looks, and for no other purpose at all.

    IOW, having prototype.then as you suggest wouldn't actually fulfill the requirement of the spec as it should be interpreted. If the spec says prototype.then, it means that prototype.then has to be used. It's not just saying there needs to be some unused figurehead placeholder present for appearances sake.

    OTOH, if we had prototype.then and it called this._then, the shadowing thing wouldn't come into play, and calling pr.then(..) really would indeed invoke the prototype.then. But again, it's for absolutely no reason as just an extra layer of abstraction and extra function call that takes up more memory and CPU. Zero benefit, some (small) cost.

  3. It's incredibly unlikely (other than sub-classing) that anyone would care about or use a prototype.then. Theoretically someone could do Promise.prototype.then.call( pr, .. ) but that's so obscure and pointless as to not be worth dedicating the extra size/memory for. Again, other than sub-classing, there is literally no reason to have then on the prototype and to use it indirectly like that.

from native-promise-only.

smikes avatar smikes commented on August 10, 2024

It's incredibly unlikely (other than sub-classing) that anyone would care about or use a prototype.then.
Theoretically someone could do Promise.prototype.then.call( pr, .. )

I agree that it's unlikely that anyone would ever do it. It's just that it's a spec requirement:

25.4.5.3 Promise.prototype.then ( onFulfilled , onRejected )
When the then method is called with arguments onFulfilled and onRejected the following steps are taken:

  1. Let promise be the this value.

from native-promise-only.

getify avatar getify commented on August 10, 2024

I think the only reason the ES6 spec is specific on where it thinks then(..) should come from is because the ES6 spec also wants to support to ES6 sub-classing (which in and of itself means that requirement belongs only in an ES6-only requirement list, and is moot/invalid pre-ES6).

Moreover, the Promises/A+ spec only says:

A promise must provide a then method to access its current or eventual value or reason

So, a promise implementation (like NPO) that doesn't put a then on some delegatable prototype is still A+ compliant, it's just not technically at the same level of compliance with ES6's larger requirements list.

As I've argued before, since there's a required trade-off in ES5 between prototype.then (which I could argue is somewhat off-handedly mentioned in ES6, at best) and the spirit of trustable external state immutability (which is sadly not talked about in either spec), it is up for debate if prototype.then is a valid requirement to enforce for ES5 implementations.

I clearly think it is not. IMO, it is a far lesser evil to lose that small compliance than it is to lose immutability. Thus, my choices in NPO.

Even granted that not everyone agrees with me on this topic, I can find no justification that the opposite opinion (that prototype.then is critical and also so is sub-classing -- immutability be damned) is strong enough to mean that ES5 implementations have to choose external mutability to meet that requirement.

At best, it should be a toss-up decision for ES5 implementations.

So, if we're going to make a test that looks for the presence of prototype and prototype.then (or any of the implied sub-classing they imply), that's fine, but it should strictly be in Layer 2 (aka ES6-only).

And it should be stressed once again that we should only test for presence, not for its behavior (as that's solely the realm of Promises/A+).

from native-promise-only.

domenic avatar domenic commented on August 10, 2024

I disagree. I think prototypes are fundamental to the language, and this immutability thing you've made up is not at all. Any ES implementation that has methods should put them on a proper prototype.

If you want a NPO-only level, that's fine. But if level 1 is supposed to validly represent the capabilities of ES5 systems, it should include that the implementation makes use of the language's model for methods and how they are shared among instances. It is literally the spec vs. your opinions here: this is not a question of the spec vs. the restrictions of ES5, as it is with subclassing.

(And of course I agree that instance bound then functions, instead of then methods on the prototype, has no bearing on A+ compliance. Just on this library's claim of being a faithful es6-shim.)

from native-promise-only.

getify avatar getify commented on August 10, 2024

@domenic

this immutability thing you've made up

It's not made up. It was a fundamental design goal of promises 5+ years ago (mid-to-late 2009). I was part of some of those early discussions (+@kriskowal) that motivated bringing the promises pattern to JS, and it was central to the discussion at that time.

I understand at this point there's no concept of external mutability that seems to have survived any of these discussions and specifications, but that doesn't mean it's made up. Indeed, I still believe it's the most fundamental tenet of why promises are useful: that they are trustable (to the extent the promise mechanism itself is trustable).

from native-promise-only.

domenic avatar domenic commented on August 10, 2024

If underscored properties are not safe enough for you, then you need to start getting much more paranoid. For example, do you ever use any members of Object.prototype in a way that could leak data or allow external forces to influence your internal state? How about Array.prototype, perhaps to track your callbacks? What if someone simply overrides a NPO promise's "then" method?

Protecting your invariants from truly malicious external consumers, i.e. the type of consumer who would mess with an underscored property, requires programming in almost an entirely different language. Indeed, it's so difficult that browser engines themselves don't implement their built-ins that way, instead using membranes to transition between a safe realm and the user realm.

from native-promise-only.

getify avatar getify commented on August 10, 2024

While I would concede that the spec doesn't clearly call it out as a design requirement, and moreover that you may not have been thinking about it when you (and others) authored the Promises/A+ spec, immutability (from the external perspective, and shallow, not deep, obviously) is an underlying spirit of the language and semantics defined.

For example:

When fulfilled, a promise: must not transition to any other state. must have a value, which must not change.
When rejected, a promise: must not transition to any other state. must have a reason, which must not change.

The state cannot be altered from the outside, or a promise implementation doesn't match that requirement. A promise value or reason cannot be altered from the outside, or a promise implementation doesn't match that requirement.

A narrower, beyond-the-words interpretation of "must not transition" or "must not change" is that the promise itself can't do the changing. If you take that (inaccurate) position, then you have to ask:

  1. If only the promise itself can't do the transitioning/changing, can the original promise creation capability change it? Can the original source of a promise decide that "Oops, I resolved that as a success, but I meant it to be an error" and thus change it's state or value? Quite obviously not.
  2. If neither the promise itself nor the original promise creation capability should be able to change these invariants about state and value, then does it make any sense at all for an external party to be able to affect them? That would be patently absurd.

The only logical conclusion is that immutability (again, shallow only) applies to all directions of influence (external and internal). A promise implementation must resist/prevent this.

If the built-in promise implementation specified by ES6 wasn't concerned about such details, why does it use internal slots for state and value? Why not just say "must be stored in a public property"?

Again, I would argue that external immutability is an underlying current to all of this design, regardless of if the current captains of the ship are thinking about it in such terms or not.

from native-promise-only.

getify avatar getify commented on August 10, 2024

If underscored properties are not safe enough for you, then you need to start getting much more paranoid.

Undescored properties are indeed a de facto "standard" for private data, but that's only because JS hasn't ever provided any other capability (other than closures -- which don't play well with prototypes) to user-land code. But that doesn't mean they are suitable or intentional, it just means they're the best hack we have.

Immutable values are, by design, already a precedent in JS. A new String("abc") or new Number(42) value cannot be mutated in any way. That's intentional, it's not just a side effect of the JS implementation that was cowpath paved.

requires programming in almost an entirely different language.

I am certainly not qualified to fully argue the case for immutable values, but it's quite clear if you browse other smart people's writings on the topic, there's plenty of merit to the usefulness of them. Indeed, it is at the fundamental design level of some other languages.

It is my opinion that JS choosing to implement promises in such a way that they are externally immutable is a good thing, because it adds more (shallow) immutable programming concepts to our language.

If your argument is that promises should be mutable, then go back and change the spec. If your argument is that promises should be immutable, but we don't (or can't!) care how strongly or weakly that is enforced, that's an invalid argument. Something is either mutable or immutable -- it cannot be in between.

To the extent that you are hampered by the limitations of a language, and must invent hacks like underscored properties (or frozen properties, for that matter) to workaround them, you sometimes do what you have to do. But that doesn't make it right.

In the land of hacks, one hack is not equal to another. Some have worse side effects than others. That much should be self-obvious studying the broader history of polyfills in JS.

If it's a question between implementing a promise polyfill which has .prototype delegation and sub-classing, or implementing a promise polyfill that chooses to adhere to the spirit of immutability, which I argue that choice clearly exists, one can make some subjective (and pseudo-objective) observations about the relative trade-offs of either decision.

I have explored these trade-offs in detail in my previously linked blog posts and write-ups, so I'm not going to restate them here.


You may not be persuaded by those arguments (you clearly aren't), but that doesn't mean that you get to reasonably dismiss my concerns as "made up". Such an attitude is hostile and unproductive, and betrays the previously assumed good faith we should be operating under to have useful discussion and make useful progress.

from native-promise-only.

domenic avatar domenic commented on August 10, 2024

Let's be clear: this is not about what type of faith the argument is being made in, or anything like that. This is about you making up requirements that aren't in the spec, for something that pretends to be polyfilling the spec, and then saying that those requirements override the ones that are in the spec. That is what I mean by made up.

from native-promise-only.

getify avatar getify commented on August 10, 2024

This is about you making up requirements that aren't in the spec

I adamantly disagree that I'm "making up requirements" that aren't in the spec. I'm drawing the conclusion that what the spec says brings, as a logical conclusion of reasoning, us back to the spirit of immutability.

If you can refute the reasoning I presented 3 messages ago, which starts at the spec wording of "must not transition" or "must not change", and ends at "no direction of influence can violate that", I invite such arguments.

If you don't like the conclusion that promises must be immutable based on what the spec already says, then go back and change the spec so it doesn't lead to such conclusions.

But the position that the immutability of a promise is irrelevant is logically indefensible. Whether you want to or not, you have to pick whether you think they are intended as mutable or immutable.

from native-promise-only.

getify avatar getify commented on August 10, 2024

@domenic btw:

Just on this library's claim of being a faithful es6-shim

...

something that pretends to be polyfilling the spec

I made no such claims. I don't think it's possible to reasonably make such a claim as an ES5 polyfill.

Let me quote myself from NPO's README:

Intent
The aim of this project is to be the smallest polyfill for Promises, staying as close as possible to what's specified in both Promises/A+ and the upcoming ES6 specification (link needed).

An equally important goal is to avoid exposing any capability for promise-state to be mutated externally. The Known Limitations section below explains the trade-offs of that balance.

from native-promise-only.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.