tc39 / proposal-get-intrinsic

EcmaScript language proposal for a way to get intrinsics.
https://tc39.github.io/proposal-get-intrinsic/
MIT License
32 stars 4 forks source link

Open Question: how to get at Symbol properties? #3

Closed ljharb closed 1 year ago

ljharb commented 3 years ago

https://npmjs.com/get-intrinsic does not offer this feature, but this proposal must.

One possibility, that I detest, is:

getIntrinsic('String.prototype[@@iterator]');

I do not think we should do this, for a number of reasons:

  1. it would expose the spec-only convention for well-known symbols, making it impossible to change later
  2. it would slightly conflict with decorators
  3. it would be confusing since @@iterator by itself is a syntax error.

We could support:

getIntrinsic('String.prototype[Symbol.iterator]');

One way to do this is to restrict what's in the brackets to be another intrinsic - ie, the spec would take what's inside the brackets, pass that into getIntrinsic, and look up that value.

However, since this only has a use case for well-known Symbols, another possibility is that we could only allow brackets to surround "Symbol dot something", where "something" is a well-known symbol name, preceded in the spec with @@.

mhofman commented 3 years ago

One way to do this is to restrict what's in the brackets to be another intrinsic - ie, the spec would take what's inside the brackets, pass that into getIntrinsic, and look up that value.

I assume the getIntrinsic used would be %getIntrinsic%, not whichever one may have replaced it in the global?

It also means a layer that adds new intrinsics, potentially including its own well-known symbols, would have to handle the square bracket parsing with the same deferral logic in its replaced getIntrinsic.

Finally a bit of an edge case, but what if there ever was an intrinsic whose value is a string that is the same as a part of an intrinsic identifier. E.g. if we had %Something.Error%, could %Something[Error.prototype.name]% be used as well?

ljharb commented 3 years ago

Yes, it'd have to be the original one, and yes, that is a good point that a wrapper would have to handle brackets itself.

E.g. if we had %Something.Error%, could %Something[Error.prototype.name]% be used as well?

One option is "yes", but another, more restrictive option is "no" (ie, if only Symbol.whatever is permitted). Since there's no use case I know of for that to work (template literals provide sufficient dynamism), it seems like the latter is a better choice.

ljharb commented 3 years ago

My intention is to resolve this in the following way:

bathos commented 2 years ago

In addition to well-known symbols, there are two other categories of (serializable) keys that bare idents don’t cover:

I’m guessing there’s no need to provide notation for the first, less sure about the second. Handling the first in realm-uri was the main reason I landed on an @@ grammar there: I was concerned that URL.prototype[Symbol.for("nodejs.util.inspect.custom")] would look too much like an arbitrary expression. Though I don’t share the distaste for @@, in the absence of the need for for support, I agree Symbol.foo is probably better.

I don’t think ES defines any (intrinsic) string keys that aren’t valid identifiers (assuming e.g. Object.name["1"] doesn’t count). In practice, there might not be hosts that do either*, so supporting arbitrary strings may be unnecessary. However it might warrant a note if so given hosts could define initial properties that getIntrinsic wouldn’t have syntax for.

* For web platform objects defined with Web IDL, the grammar for “identifier” almost ensures every string-key property found among the initial objects would also have to match IdentifierName. It departs by allowing hyphens, even as the first character. That seems to have been motivated by a metaprogram-y part of CSSStyleDeclaration’s definition which implicitly declares n interface mixins with a single attribute that has such a name. I can’t find anywhere else that uses hyphens — and in any case neither Chromium nor FF implement the ones I linked to. Both have the generic “named property” getter (→ exotic [[Get]] at instance level) only, not the attributes (→ accessor properties on prototype).

ljharb commented 2 years ago

I’m confused; global symbols aren’t intrinsics, and every intrinsic has an identifiername.

ljharb commented 2 years ago

If another specification wants to provide intrinsics whose path convention doesn’t match 262, then it’s incumbent on that spec to define how it’s wrapping getIntrinsic.

bathos commented 2 years ago

I’m confused; global symbols aren’t intrinsics

In other threads it seemed you and other folks used the term “intrinsics” both in the realm.[[Intrinsics]] sense and as a general term for analogous concepts like Web IDL’s “initial objects”. I would have thought the values some spec/host/environment deemed “intrinsic-like” could include any value. The example I gave was a property path for a globally exposed Node built in which a user might expect to be able to retrieve via this API in a Node env. However I wasn’t providing it as an example to demonstrate a need: I was just listing the remaining groups of serializable keys that don’t have (and: may not need) a defined syntax.

and every intrinsic has an identifiername.

(Sticking with the ECMA-262-only sense,) I had thought that only the well-known intrinsics had names, but I might be using the terms wrong. For example I thought %TypedArray% was a “well-known” (and has a name), while %TypedArray.prototype% was not (but was still an intrinsic + could still be expressed using “% notation”). Likewise I figured the %TypedArray%[@@species] getter was an “intrinsic” too (non-well-known) even though the (spec’s) “% notation” isn’t able to reference it.

If another specification wants to provide intrinsics whose path convention doesn’t match 262, then it’s incumbent on that spec to define how it’s wrapping getIntrinsic.

Ah, that was missing from my picture (though it wouldn’t have been if I’d looked at the spec text first, ooops. I didn’t realize it was already written). The reason I was thinking these things would matter (even if it was just to say concretely “this will not be supported”) is that I imagined the proposal defining hooks of some kind as an extension point for hosts. Now that I realize it’s sufficient for them to just extend the behavior (including, possibly, its “grammar”) without that, I can see that the things I brought up are kinda “non-questions”.

ljharb commented 2 years ago

I'm only talking about ecma262's term of "intrinsics", which does not include Symbols in the registry (because none exist in a newly born realm).

I completely agree that %TypedArray.prototype% is an intrinsic - one where each part of it is an IdentifierName, split by the dots.

I also agree that, say, Array.prototype[Symbol.iterator] is an intrinsic - one where one of the parts is not an IdentifierName. This issue is asking how we should permit access to that intrinsic in terms of the syntax we accept in the function argument string.

%TypedArray.prototype[Symbol.species]% is relevant to this issue, because it's a Symbol - but it's also relevant to #4, since it's a getter.

This proposal is not defining any hooks; it's just a function. Hosts will be permitted to provide their own function instead, but ideally they will be normatively constrained so they can't deviate in undesirable ways from the patterns that 262's getIntrinsic supports.