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

doc: added symbols guidelines #22684

Closed
wants to merge 1 commit into from

Conversation

mcollina
Copy link
Member

@mcollina mcollina commented Sep 4, 2018

I've added the Symbol guideline that spun out from the discussion in #20857.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • documentation is changed or added
  • commit message follows commit guidelines

@nodejs-github-bot nodejs-github-bot added the doc Issues and PRs related to the documentations. label Sep 4, 2018
@mcollina
Copy link
Member Author

mcollina commented Sep 4, 2018

cc @devsnek @nodjes/tsc.

This also is relevant for #22302 (feature detection).

}
```

Local symbols makes it harder for developers to monkey patch/access
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: s/makes/make

@richardlau
Copy link
Member

cc @devsnek @nodjes/tsc.

This also is relevant for #22302 (feature detection).

@nodejs/tsc 😄

@mcollina mcollina added the tsc-agenda Issues and PRs to discuss during the meetings of the TSC. label Sep 4, 2018
@mcollina
Copy link
Member Author

mcollina commented Sep 4, 2018

Tagging it tsc-agenda to raise awareness. cc @nodejs/tsc.

@mcollina mcollina requested a review from a team September 4, 2018 20:42
@mcollina mcollina added the meta Issues and PRs related to the general management of the project. label Sep 4, 2018
Copy link
Contributor

@cjihrig cjihrig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of the content could probably be replaced with a link to MDN, but LGTM. A link to MDN probably wouldn't hurt either way though 😄

it is often used for metaprogramming purposes, as they can be used as
property keys like string. There are two types of
symbols, local and globals. Symbols are not included in
JSON.stringify() but thay are by `util.inspect()`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/thay/they/

In the Node.js runtime we prefix all our global symbols with `nodejs.`,
e.g. `Symbol.for('nodejs.hello')`.

Globals symbols should be preferred to allow customizations of certain
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/Globals/Global/

I suggest to rephrase this sentence to something like: Global symbols are used in Node.js to allow customization of certain behaviors, i.e., metaprogramming.

doc/guides/using-symbols.md Outdated Show resolved Hide resolved
# Using global symbols

ES6 introduced a new type: `Symbol`. This new type is _immutable_, and
it is often used for metaprogramming purposes, as they can be used as
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as they is a bit confusing since there are no plurals before (except purposes).

ES6 introduced a new type: `Symbol`. This new type is _immutable_, and
it is often used for metaprogramming purposes, as they can be used as
property keys like string. There are two types of
symbols, local and globals. Symbols are not included in
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

globals -> global?

doc/guides/using-symbols.md Outdated Show resolved Hide resolved
private fields, as they require more work than a property prefixed
with an `_`.

## Global symbols - `Symbol.for`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symbol.for -> Symbol.for() ?

ES6 introduced a new type: `Symbol`. This new type is _immutable_, and
it is often used for metaprogramming purposes, as they can be used as
property keys like string. There are two types of
symbols, local and globals. Symbols are not included in
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symbols in the GlobalSymbolRegistry have no intrinsic differences from symbols created through Symbol() -- other than that it is in a global map. There are no two "types" of symbols.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a user point of view, those are two different things. It might well be the same type, but a) I cannot add a symbol that I created to the global map and b) I cannot remove a symbol from the global map. Specifically Symbol('hello') !== Symbol('hello') and Symbol.for('hello') === Symbol.for('hello'): this alone makes me think that they are two different things. Would you mind to suggest some different wording?

doc/guides/using-symbols.md Outdated Show resolved Hide resolved
symbols, local and globals. Symbols are not included in
JSON.stringify() but thay are by `util.inspect()`.

## Local `Symbol`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no such thing as a "local" Symbol. Just say symbols created using the Symbol() function.


Local symbols make it harder for developers to monkey patch/access
private fields, as they require more work than a property prefixed
with an `_`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Say why this is good. If I were an unsuspecting developer I might consider

make it harder for developers to monkey patch/access private fields

a bad thing and question why Node.js is doing this.

In the Node.js runtime we prefix all our global symbols with `nodejs.`,
e.g. `Symbol.for('nodejs.hello')`.

Globals symbols should be preferred to allow customizations of certain
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive home the fact that the symbol is intended developer-facing.

Global symbols should be preferred when a developer-facing interface is needed to allow behavior customization, i.e., metaprogramming.

## Global symbols - `Symbol.for`

Global symbols are created with `Symbol.for(string)`. These symbols are
stored in a global registry and they can be easily retrieved. However,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd also clarify what "global" mean here: they are global to all contexts (including VM contexts) in the same process/JavaScript agent/V8 Isolate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would just say v8 isolate

For this reason, we often use them to simulate private fields, like so:

```js
const kField = Symbol('field');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make this Symbol('kField'). Having consistent naming is so helpful for grep-ping, and I’ve been changing it to this pattern whenever I touched these lines.

@mcollina
Copy link
Member Author

mcollina commented Sep 5, 2018

PTAL @TimothyGu @addaleax.

monkey-patchable make maintaining and evolving Node.js harder, as private
properties are not documented and can change within a patch release.
Some extremely popular modules in the ecosystem monkey patch some
internals, making it impossible for us to update and imoprove those
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/imoprove/improve/

properties are not documented and can change within a patch release.
Some extremely popular modules in the ecosystem monkey patch some
internals, making it impossible for us to update and imoprove those
areas without breaking a significant amount of users.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-breaking +causing issues for

@mcollina mcollina removed the tsc-agenda Issues and PRs to discuss during the meetings of the TSC. label Sep 5, 2018
@danbev
Copy link
Contributor

danbev commented Sep 7, 2018

Landed in 4f5aa36.

@danbev danbev closed this Sep 7, 2018
danbev pushed a commit that referenced this pull request Sep 7, 2018
PR-URL: #22684
Reviewed-By: Tobias Nießen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Vse Mozhet Byt <[email protected]>
Reviewed-By: Daniel Bevenius <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Anatoli Papirovski <[email protected]>
@mcollina mcollina deleted the symbols-guideline branch September 7, 2018 07:59
tniessen added a commit to tniessen/node that referenced this pull request Sep 7, 2018
targos pushed a commit that referenced this pull request Sep 7, 2018
PR-URL: #22684
Reviewed-By: Tobias Nießen <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Vse Mozhet Byt <[email protected]>
Reviewed-By: Daniel Bevenius <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Anatoli Papirovski <[email protected]>
@tniessen
Copy link
Member

tniessen commented Sep 7, 2018

@BridgeAR suggested to make symbols non-enumerable for private fields in #22747 (comment). If we decide to do that, we should probably add to these guidelines, so I would love to hear opinions.

@mcollina
Copy link
Member Author

mcollina commented Sep 7, 2018

I’m not super fond of that, because we would have to use defineProperty instead of a basic assignment in class definitions and in a lot of property assignment. It’d make the code less maintainable over time and it might cause some performance loss.

@jasnell
Copy link
Member

jasnell commented Sep 7, 2018

I'm not particularly fond of it either. While I was not really happy with the change that caused symbols to be included in the inspect output, if we really want to hide those details we can do so by providing a custom inspect implementation.

@BridgeAR
Copy link
Member

BridgeAR commented Sep 7, 2018

I don't think that the code becomes less maintainable by using Object.defineProperty but it would likely be a tiny performance overhead in case it's a hot function. The assignment itself is normally in the constructor and that should be fine.

A custom inspect function is not ideal since we would have to reimplement a couple of features every time and that's significantly more work and code than just switching to non-enumerable properties.

@mcollina
Copy link
Member Author

mcollina commented Sep 8, 2018

The refactoring needed to https://github.com/nodejs/node/blob/master/lib/internal/http2/core.js would be massive, and definitely hinder its readability.

As an example

[kInit](id, handle) {
const state = this[kState];
state.flags |= STREAM_FLAGS_READY;
const session = this[kSession];
session[kState].pendingStreams.delete(this);
session[kState].streams.set(id, this);
this[kID] = id;
this[async_id_symbol] = handle.getAsyncId();
handle[kOwner] = this;
this[kHandle] = handle;
handle.ontrailers = onStreamTrailers;
handle.onstreamclose = onStreamClose;
handle.onread = onStreamRead;
this.uncork();
this.emit('ready');
}
[kInspect](depth, opts) {
const obj = {
id: this[kID] || '<pending>',
closed: this.closed,
destroyed: this.destroyed,
state: this.state,
readableState: this._readableState,
writableState: this._writableState
};
return `Http2Stream ${util.format(obj)}`;
}
would need to be moved out of the class definition, because we cannot declare non-enumerable fields there.

tniessen added a commit to tniessen/node that referenced this pull request Sep 12, 2018
PR-URL: nodejs#22770
Refs: nodejs#22684
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: James M Snell <[email protected]>
targos pushed a commit that referenced this pull request Sep 14, 2018
PR-URL: #22770
Refs: #22684
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: James M Snell <[email protected]>
targos pushed a commit that referenced this pull request Sep 15, 2018
Define `util.inspect.custom` as
`Symbol.for("nodejs.util.inspect.custom")` rather than
`Symbol("util.inspect.custom")`. This allows `inspect` hooks to
easily/safely be defined in non-Node.js environments.

Fixes: #20821
Refs: #22684

PR-URL: #20857
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
Reviewed-By: Sakthipriyan Vairamani <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: John-David Dalton <[email protected]>
targos pushed a commit that referenced this pull request Sep 19, 2018
PR-URL: #22770
Refs: #22684
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: James M Snell <[email protected]>
targos pushed a commit that referenced this pull request Sep 20, 2018
PR-URL: #22770
Refs: #22684
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: Luigi Pinca <[email protected]>
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Colin Ihrig <[email protected]>
Reviewed-By: James M Snell <[email protected]>
targos pushed a commit to targos/node that referenced this pull request Sep 23, 2018
Define `util.inspect.custom` as
`Symbol.for("nodejs.util.inspect.custom")` rather than
`Symbol("util.inspect.custom")`. This allows `inspect` hooks to
easily/safely be defined in non-Node.js environments.

Fixes: nodejs#20821
Refs: nodejs#22684

PR-URL: nodejs#20857
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
Reviewed-By: Sakthipriyan Vairamani <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: John-David Dalton <[email protected]>
targos pushed a commit that referenced this pull request Sep 24, 2018
Define `util.inspect.custom` as
`Symbol.for("nodejs.util.inspect.custom")` rather than
`Symbol("util.inspect.custom")`. This allows `inspect` hooks to
easily/safely be defined in non-Node.js environments.

Fixes: #20821
Refs: #22684

Backport-PR-URL: #23039
PR-URL: #20857
Reviewed-By: Ruben Bridgewater <[email protected]>
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Tiancheng "Timothy" Gu <[email protected]>
Reviewed-By: Sakthipriyan Vairamani <[email protected]>
Reviewed-By: James M Snell <[email protected]>
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Michaël Zasso <[email protected]>
Reviewed-By: John-David Dalton <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc Issues and PRs related to the documentations. meta Issues and PRs related to the general management of the project.
Projects
None yet
Development

Successfully merging this pull request may close these issues.