Skip to content
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

Open Question: what happens with a missing intrinsic? #6

Closed
Tracked by #1
ljharb opened this issue Sep 1, 2021 · 28 comments · Fixed by #16
Closed
Tracked by #1

Open Question: what happens with a missing intrinsic? #6

ljharb opened this issue Sep 1, 2021 · 28 comments · Fixed by #16
Labels
question Further information is requested

Comments

@ljharb
Copy link
Member

ljharb commented Sep 1, 2021

When the argument to getIntrinsic does not result in a result being returned, what should it do?

Options:

  • undefined
  • throw an exception (TypeError if we're boring, SyntaxError if we're whimsical, etc)
  • a sentinel value (probably not; this has been rejected for other proposals)
@ljharb ljharb added the question Further information is requested label Sep 1, 2021
@mhofman
Copy link
Member

mhofman commented Sep 1, 2021

I'd vote undefined, the same way getOwnPropertyDescriptor does.

@ljharb
Copy link
Member Author

ljharb commented Sep 1, 2021

How would a user differentiate getIntrinsic('String.prototype.yogurt') (never a thing) from getIntrinsic('String.prototype.at') (a thing, but not implemented in this engine yet)?

@mhofman
Copy link
Member

mhofman commented Sep 1, 2021

I'm not sure I understand how the implementation would know that a thing is a specified thing that it just hasn't implemented yet.

@ljharb
Copy link
Member Author

ljharb commented Sep 1, 2021

Ha, that’s a fair point.

@mhofman
Copy link
Member

mhofman commented Sep 28, 2021

Ok thinking about this more, there is actually a very valid reason to not return undefined. The value of the intrinsic may be undefined. I don't believe any parts of the spec actually does this, but who knows in the future?

@ljharb
Copy link
Member Author

ljharb commented Sep 28, 2021

It's technically the case for every setter in the language :-)

This would be a problem for any sentinel value, unless we use a container, which has poor ergonomics.

@mhofman
Copy link
Member

mhofman commented Sep 28, 2021

It's technically the case for every setter in the language :-)

Fair.

This would be a problem for any sentinel value, unless we use a container, which has poor ergonomics.

Is there actually any exposed as an intrinsic value? Agreed a container is terrible ergonomics, but having to wrap in try catch isn't particularly great either.

@ljharb
Copy link
Member Author

ljharb commented Sep 29, 2021

Any what, undefineds? there's globalThis.undefined - ie, %undefined% is itself an intrinsic value, not a keyword.

@rdking
Copy link

rdking commented Nov 2, 2021

I'd want to throw a SyntaxError or ReferenceError(not sure if this option makes sense) if the requested intrinsic is not in spec. If, however, it is in spec, but not implemented yet, then return undefined as in void 0;

@mhofman
Copy link
Member

mhofman commented Nov 2, 2021

I'd want to throw a SyntaxError or ReferenceError(not sure if this option makes sense) if the requested intrinsic is not in spec. If, however, it is in spec, but not implemented yet, then return undefined as in void 0;

I'm not sure I understand how the implementation would know that a thing is a specified thing that it just hasn't implemented yet.

@ljharb
Copy link
Member Author

ljharb commented Nov 2, 2021

Indeed; every infinite possibility is potentially “not implemented yet”, so when it isn’t providing the expected intrinsic, it can only do one thing: throw, or return a sentinel.

@rdking
Copy link

rdking commented Nov 2, 2021

Pretty straight forward on how to do that. The spec exists, right? If a version of an ES engine conforms to a given spec, then it should have every required capability of that spec. The developers of that engine would then be responsible for tracking the unimplemented portions of the spec and coding accordingly. It's not that the engine would calculate this, but rather that this would be hard coded in the engine.

@rdking
Copy link

rdking commented Nov 2, 2021

That just triggered a new thought. Maybe there should also be an API for retrieving the ES spec version that the engine supposedly conforms to.

@mhofman
Copy link
Member

mhofman commented Nov 2, 2021

Do you mean that an engine has to decide it implements a specific spec version and stick to it strictly? How would it implement stage 3 proposals in that case?

@ljharb
Copy link
Member Author

ljharb commented Nov 2, 2021

The implementation can’t know what will be added to the spec tomorrow, and the spec does not actually exist from a runtime perspective. If the engine knows about something, it implements it.

@ljharb
Copy link
Member Author

ljharb commented Nov 2, 2021

Engines do not, and will never, strictly strictly adhere to any “version” of the spec, nor is there any such version - it’s a living standard, and the latest version is whatever is on the default branch on GitHub. The yearly editions are merely snapshots.

@rdking
Copy link

rdking commented Nov 2, 2021

What I mean is that an engine can conform to ES6, while still implementing ES7 features. If the requested intrinsic is an ES6 feature, but that feature is not available, return void 0. If however, the requested intrinsic is an ES7 feature not implemented, then throw. Just because the engine claims to conform to a specific version of the spec doesn't mean it doesn't implement proposals or versions beyond that. It would only mean that a specific spec version was the primary target of this engine release. Strict adherence isn't required for this to work, only strict transparency about what does and doesn't exist.

@ljharb I get that the spec is a "living standard". However, that "living standard" changes in surges and gets labelled when it does: ES3, ES5, ES6, ES2015, ES2017, etc.... Whether you want to call it that or not, that's versioning. Pair that with a date code, and you'll know the state of the specification that a given engine is trying to conform to. Isn't the spec document in a GIT repo? Again versioning. I'm just saying make use of it. It's a piece of meta information that would be useful to developers.

@ljharb
Copy link
Member Author

ljharb commented Nov 2, 2021

Engines don’t claim to, nor actually, conform to a specific version of the spec, and never have.

Engines would never agree to this requirement - it’s a nonstarter.

If an intrinsic isn’t present, whether because it’s not standardized yet or not implemented yet, it must only behave one way. This issue is about which way that will be.

@rdking
Copy link

rdking commented Nov 3, 2021

Unfortunate. Oh well. Under those restrictions, seems undefined is the only rational choice. That seems to be the defacto for all non-existent things that are directly being used.

@JakeChampion
Copy link

I think a sentinel would be my preferred option, such as a new well-known symbol Symbol.notImplemented

@ljharb
Copy link
Member Author

ljharb commented Dec 22, 2021

@JakeChampion then what happens with getIntrinsic('%Symbol.notImplemented%')?

@mhofman
Copy link
Member

mhofman commented Dec 22, 2021

Since we might need an enumeration style API, would it make sense to structure getIntrinsic more as a Map, where you can independently test for has ?

@ljharb
Copy link
Member Author

ljharb commented Dec 22, 2021

@mhofman I very much do not think we might ever need an enumeration style API, and this proposal explicitly is not providing one (and i suspect @erights would block it if it provided one).

Since Maps are impossible to make immutable, I'm not sure how we'd even provide such an API. The use case this proposal is solving is solely "get a single known intrinsic".

@mhofman
Copy link
Member

mhofman commented Dec 22, 2021

Let's discuss the enumeration need separately, I may have a use case that requires something like it.

Need for enumeration aside, I wasn't thinking of a Map instance per-se, but more of a Map style thing that allows test of content as well as retrieving content, probably something like a frozen object with only own methods.

@ljharb
Copy link
Member Author

ljharb commented Dec 22, 2021

That’s a pretty big new primitive (in the language sense) to design, when a) all the current use cases only require getting a single known intrinsic, and b) an enumeration use case would probably merit an iterator rather than a large in-memory construct.

@rdking
Copy link

rdking commented Dec 23, 2021

I'm curious as to why it has to be a new primitive when a frozen object satisfies the described requirement fully? Isn't Object.entries() already suitably iterable and Object.prototype.hasOwnProperty() good enough for content testing?

@ljharb
Copy link
Member Author

ljharb commented Dec 23, 2021

@rdking that's a fair point, that it'd make more sense as a null object. but that still would require a large observable in-memory object, which none of the motivating use cases require.

@mhofman
Copy link
Member

mhofman commented Dec 23, 2021

There's also the problem that one would need to capture Object.entries() and Object.prototype.hasOwnProperty() to protect against modifications of those intrinsics!

@ljharb ljharb mentioned this issue Oct 26, 2022
24 tasks
ljharb added a commit that referenced this issue Jan 17, 2023
Fixes #2. Closes #3. Closes #4. Closes #6.
@ljharb ljharb closed this as completed in b8386d7 Jan 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants