tc39 / proposal-extensions

Extensions proposal for ECMAScript
MIT License
150 stars 6 forks source link

Make shadowing an early error but not a new namespace #4

Open Jack-Works opened 3 years ago

Jack-Works commented 3 years ago

If the only motivation for isolated namespace is to prevent shadowing, I think we can make shadowing extensions becomes an early error.

ljharb commented 3 years ago

I'm confused about that as a motivation, since linters handle that. What's wrong with shadowing?

Jack-Works commented 3 years ago

I don't know, what's it seems like avoid shadowing is one of the main reason of introducing a new namespace. @hax can you explain?

hax commented 3 years ago

Yeah, I understand this is a uncommon design in our language. The other case is numeric literal suffixes and a potential case is decorators. I'd like to write a separate document to discuss the problem in a much general way.

ljharb commented 3 years ago

Decorators as second class values was soundly rejected by the committee, as i understand it. I don’t think it’s a good approach.

hax commented 3 years ago

@ljharb I agree second-class values is not a good idea for JS, but the cases I'm discussing is not second-class values, they are still first-class values, but could be declared/imported into a parallel lexical scope.

ljharb commented 3 years ago

Let me rephrase: a parallel scope was also part of the rejected static decorators proposal, and i also don’t think that’s a good approach.

Jack-Works commented 3 years ago

IIRC @hax has explained his reason to me in person.

The main reason is, these kinds of values (extension methods, numerical literals, decorators, ...) are very specialized uses. This means developers only want to use the value in this way (as a decorator, not a normal function). Any other references to these specialized values are highly likely to be a mistake.

But I have a question to @hax. What if I want to "compose" two extensions? (Use them as values, not in the extension space). Let's say, for example, use :: as the assumed grammar.

// extension.js
export function B() { ... }
export function A() { ... }
// use.js
import ::{ A, B } from './extension'
// This make A and B imported in the extension namespace
import { composeExtensions } from 'my-awesome-utils'

const C = composeExtensions(A, B)
// Oops, ReferenceError. I can't compose them
// cause they're in the extension namespace.
// lets-try-again.js
import { A, B } from './extension'
// In normal namespace
import { composeExtensions } from 'my-awesome-utils'

const C = composeExtensions(A, B)
// OK

"test"::C() // Oops, ReferenceError, C is not in the extension scope.
// finally.js
import { A, B } from './extension'
// In normal namespace
import { composeExtensions } from 'my-awesome-utils'

const C = composeExtensions(A, B)
const ::C = C
// Re-introduce a value to another namespace?
// Looks strange

"test"::C() // No error.
ljharb commented 3 years ago

@Jack-Works I’m confused, i only brought up decorators as an example of second-class values and parallel scopes being subpar.

I’m not sure how your example relates to extensions.

Jack-Works commented 3 years ago

@ljharb Hax's reason is applied across proposals (extension methods, numerical literals, decorators, ...). If my example confuses you, let me change my example to extension flavor. You can see how they're very identical in some way.

ljharb commented 3 years ago

Decorators, just like template tag literals, will just be functions. If you want a function to only be run as a decorator or as a template tag, you’d have to manually write code to ensure that - just like you’d have to to prevent your function from being used as a decorator or a template tag.

I don’t want to be forced to create a variable - in a magic second scope or not - just to be able to safely extract and borrow a prototype method.

hax commented 3 years ago

I understand JS traditionally leave all such responsibility to developers, unfortunately some features would become very impractical if the common cases would take some short and common names.

The best example is numeric suffix. A use case of it is polyfill of 1n 1d, which will take the common one char name like n or d, and suffer shadow issue seriously. This is why numeric suffix proposal have to choose second scope.

Both numeric suffix or extensions could be used to for units, a common case is CSS units, which also have many short names, like px, pc, s, deg, Q, etc. (And note we always need special treatment for in unit).

Decorators may be minor, so by itself may be ok to not use parallel scope. But generally speaking, they are similar. If numeric suffix and extensions use parallel scope, we could reconsider decorators to make the pattern consistent across features and make the mental model simpler.

ljharb commented 3 years ago

It's not just a tradition; it's an intentional design choice. That a second scope would address it doesn't mean that's a viable solution, for this or any other proposal.

hax commented 3 years ago

What if I want to "compose" two extensions?

This is a high-level usage which mostly only used by advanced users, and u could always do composing in a separate module. And actually u should, because it's just composing and have no relationship to extensions.

Note the difference with fp style which composing is relative common, in extensions(OO) style, normally extensions are defined in libraries or separate module.

This is also common practice in decorators --- decorators are declared in separate modules.

hax commented 3 years ago

It's not just a tradition; it's an intentional design choice.

@ljharb There are many "intentional" design in the past and cause ux problems --- as JS was intentionally designed only for small scripts. I think we already fixed (or tried to fix) some past "intentional" design in every new versions intentionally. So this could be the new one. 😅