import.meta and TC39 process as a whole
An HTML attachment was scrubbed... URL: esdiscuss/attachments/20170803/4c09676b/attachment
Dmitrii, as stated by the champion of import.meta in the issue, issues/2 is one step of the process, in which we ask other members of the committee to review the spec text before it can be presented for stage 3. But you should be able to open other issues in that repo to voice your concerns about that proposal. Additionally, we have other channels, like this one, or via other members who can voice your concerns in upcoming meetings.
As for the particular feature that you’re referencing, I suggest you to look at previous discussions about import
, and why it is different (a hint: it is different because like super, it needs some contextual information). As one of the champions of the whatwg loader, I can tell you that we spent many hours trying to figure the best course of actions based on the initial loader spec, and we believe import
is the right thing to do. import.meta is just a progression of that decision.
whatwg/loader was too big of a spec. It was floated around in various forms for at least 5 years. Despite the very hard work of its champions it didn't garner enough implementer support. I think history has proven now that incremental improvements are more likely to succeed, so I'm happy to see import() and import.meta be able to go through the process at a relatively swift pace.
the rushed speccing of es6 modules is partly the reason people like me decided to join this discussion to voice opposition and provide inertia against such hugely disruptive changes in frontend programming.
i see this as a feature that primarily benefits companies with large code-bases and frontend teams. its far too complicated to be useful for majority of smaller projects with 1-3 frontend devs, where attaching libraries to global namespace is not problematic at all.
their concerns were dismissed with no argument
I’m curious what the concerns were. You mentioned disliking the syntax, but I’m guessing there’s more to it than that?
I’ve been experimenting with ES modules over HTTP 2 for a few months. I used rollup to create my dep graph without actually bundling, then served requested modules as entry points with a server push for their deps. I imagine that it won’t be long before generic tooling for this sort of approach emerges (my own solution is pretty hacky, just wanted to see how it might work).
I love how it’s turned out so far. I haven’t used dynamic import yet though, and this is the first time I’m seeing complaints about web standards development being too efficient.
I’m curious what the concerns were. You mentioned disliking the syntax, but I’m guessing there’s more to it than that?
the concern is that es modules are starting to look like a solution in search of a problem. its redundant and unnecessary on the server-side. and it continues to fail to solve an relevant pain-point for everyday programmers on the frontend-side now, or in the foreseeable future, while creating new ones.
I’ve been experimenting with ES Modules over HTTP 2 for a few months. I used rollup to create my dep graph without actually bundling, then served requested modules as entry points with a server push for their deps. I imagine that it won’t be long brolefore generic tooling for this sort of approach emerges (my own solution is pretty hacky, just wanted to see how it might work).
for most projects, dep-graph and tree-shaking have marginal benefits in frontend programming, given their complexity. for all that extra work and boilerplate, the result is typically not anymore smaller, more efficient, or more maintainable than a pre-es6 rollup file.
Inline
On Fri, Aug 4, 2017, 02:47 kai zhu <kaizhu256 at gmail.com> wrote:
I’m curious what the concerns were. You mentioned disliking the syntax, but I’m guessing there’s more to it than that?
the concern is that es modules are starting to look like a solution in search of a problem. its redundant and unnecessary on the server-side. and it continues to fail to solve an relevant pain-point for everyday programmers on the frontend-side now, or in the foreseeable future, while creating new ones.
First, it would be very nice on the server side and in CLI applications to have a more efficient module system to implement, especially for larger applications and in desktop applications being built with Electron and NW.js, where startup time actually matters, and engines are presented with the opportunity to garbage collect unused parts of modules.
Second, it pays off in spades on client side, due to much smaller bundles. We already have features primarily targeting browsers (shared array buffers and atomics - Node doesn't have threading support), so there's precedent, and I feel even if it has no value anywhere else, the need in the client side is more than enough. Oh, and having a module implementation built-in alleviates the need for a separate script loader, and it makes prototyping easier.
So there are pain points that having a good native module and script loader would solve very well.
I’ve been experimenting with ES Modules over HTTP 2 for a few months. I used rollup to create my dep graph without actually bundling, then served requested modules as entry points with a server push for their deps. I imagine that it won’t be long brolefore generic tooling for this sort of approach emerges (my own solution is pretty hacky, just wanted to see how it might work).
for most projects, dep-graph and tree-shaking have marginal benefits in frontend programming, given their complexity. for all that extra work and boilerplate, the result is typically not anymore smaller, more efficient, or more maintainable than a pre-es6 rollup file.
Just because a feature exists doesn't mean you have to use it. I almost never use proxies or shared memory/atomics, because I don't need them. I also rarely use classes. You aren't obligated to use a feature you don't like - there's a lot of people who avoid classes like the plague.
I’ll reply to several emails with one, so as not to spread more-or-less similar texts over multiple small emails.
whatwg/loader was too big of a spec. It was floated around in various forms for at least 5 years. Despite the very hard work of its champions it didn't garner enough implementer support.
And the reason is: it was supposed to be a properly designed spec. That is why it was big. Because you either do it properly, or not at all
I think history has proven now that incremental improvements are more likely to succeed, so I'm happy to see import() and import.meta be able to go through the process at a relatively swift pace.
No. These are not incremental improvements. These are ad-hoc solutions that beget more and more ad-hoc solutions. Calling it “incremental improvements” is putting slapstick on a pig.
Let’s take dynamic imports as a prime example of a horrible no-good ad-hoc solution and how it begat a horrible no-good ad-hoc solution in the form of import.meta.
import is a keyword. It’s scope and usage are known, simple, and understandable.
What is import()? It looks like a function, it behaves like a function (it’s invoked like a function, it accepts parameters like a function, it returns values like a function). Except it’s not a function. In some contexts it is a function:
import(‘blabla’).then(() => …)
In other contexts it is not:
[‘bla’, ‘bla’].map(import) and others
To quote myself from an issue: "So, what is this new import? A new keyword? No. A builtin function on par with eval, parseInt et al? Why does it override the name of an existing keyword? Wouldn't it be better to introduce module-related functionality into a new global Module object exposing Module.import, Module.importAsync etc.?”
Well, the only somewhat-valid argument for the dynamic import is this:
I suggest you to look at previous discussions about import, and why it is different (a hint: it is different because like super, it needs some contextual information).
Let’s consider this:
-
super() is a new function-like keyword that doesn’t override behaviour of an existing one. It has its clearly defined place where it can be used, and, well, it is a function call as it ends up calling user-defined functions (etc. etc. etc., see tc39/proposal-dynamic-import#23)
-
the argument for context is especially funny considering how import.meta started
First of all, this is a language you design. There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Second. Let’s see how import.meta proposal started (esdiscuss.org/notes/2017-05-23#16iiia-accessing-host-specific-module-metadata-from-inside-a-module):
> Probably a good idea to do import * as module from "js:context"
See? Since you scrapped proper implementation in favour of an ad-hoc solution eschewing common sense, now you scramble to find ad-hoc solutions to your ad-hoc solutions. Let’s just for a second assume you went with Module.import(). All of a sudden you’re free to extend Module with a Module.context or Module.meta, define it as Introspection API and have fun with it. Instead, you go for this (can’t find the link right now) in favour of import.meta:
> Yes, we have new.target, super.* and function.sent as precedent
That is, you take horrible ad-hoc design decisions and present them as viable precedent.
This morning literally in the span of time between stepping out of the shower and finishing my morning shave I came up with a solution that does away with new.target, function.sent and import.meta.
It’s called System.context (especially useful with tc39/proposal-optional-chaining)
System.context.function.{sent, target, what,have,you}, System.context.module.{whatever,meta,info,you,might,want,to,slap,on}
See. Suddenly you have an infinitely extensible introspection API that you can (ab)use to your heart’s content instead of sodomizing the rest of the language. But that would require some sort of long-term thinking instead of ad-hoc (sorry, incremental) short-term solutions.
There is a reason PHP became a laughing stock in programming community. However, it took way more than 10 years to arrive at mysql_real_escape_string and \ as namespace separator. At the current rate JS will surpass PHP within a year or two.
On Fri, 04 Aug 2017 at 08:46 kai zhu
< mailto:kai zhu <kaizhu256 at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
I’m curious what the concerns were. You mentioned disliking the syntax, but I’m guessing there’s more to it than that?
the concern is that es modules are starting to look like a solution in search of a problem. its redundant and unnecessary on the server-side. and it continues to fail to solve an relevant pain-point for everyday programmers on the frontend-side now, or in the foreseeable future, while creating new ones.
I’ve been experimenting with ES Modules over HTTP 2 for a few months. I used rollup to create my dep graph without actually bundling, then served requested modules as entry points with a server push for their deps. I imagine that it won’t be long brolefore generic tooling for this sort of approach emerges (my own solution is pretty hacky, just wanted to see how it might work).
for most projects, dep-graph and tree-shaking have marginal benefits in frontend programming, given their complexity. for all that extra work and boilerplate, the result is typically not anymore smaller, more efficient, or more maintainable than a pre-es6 rollup file.
Myself, and tens of programmers I know, use ES6 modules (and their precursors, CommonJS modules) for years now and can't even believe there was a time when they didn't exist, given that they have totally transformed (in a good way) the way we work. And that is also the vibe I am getting from the community (twitter, blog posts, meetups, etc). So when you say that modules are "redundant and unnecessary on the server-side. and [...]continue to fail to solve an relevant pain-point for everyday programmers on the frontend-side now", I believe you are not talking about myself or about the community I surround myself with.
- Gil Tayar
There’s nothing stopping you from providing context to System.load. Or
Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
Could we please remember to keep posts on the list professional, constructive, and courteous?
I'm seeing a lot of frustration manifesting as rudeness here. The former is understandable. The latter is not constructive.
Thank you,
-- T.J. Crowder
Make “System” syntax, and there you go.
Instead we have multiple ad-hoc random additions to random keywords just because someone needs something and since there are rarely any long-term design decisions anymore, we’re stuck with new.target, function.sent, import.meta (add your own)
Seriously. How is new.target is “syntax that has context information”, but System.whatever cannot be provided with context information because it’s API?
On Fri, 04 Aug 2017 at 09:26 Jordan Harband
< mailto:Jordan Harband <ljharb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
It can't be made syntax, because var System = {};
is valid code, and we
can't break the web. (seriously)
But you can’t var x = import
, for example. I guess you can’t var import = {}
either.
Hmmm… I wonder why…
On Fri, 04 Aug 2017 at 09:50 Jordan Harband
< mailto:Jordan Harband <ljharb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
It can't be made syntax, because var System = {};
is valid code, and we can't break the web. (seriously)
On Fri, Aug 4, 2017 at 12:31 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Make “System” syntax, and there you go.
Instead we have multiple ad-hoc random additions to random keywords just because someone needs something and since there are rarely any long-term design decisions anymore, we’re stuck with new.target, function.sent, import.meta (add your own)
Seriously. How is new.target is “syntax that has context information”, but System.whatever cannot be provided with context information because it’s API?
On Fri, 04 Aug 2017 at 09:26 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
Because it's been reserved syntax since JavaScript's inception, and System hasn't.
I'd recommend some light reading before attempting to continue arguing: mathiasbynens.be/notes/reserved
On Fri, Aug 4, 2017 at 8:53 AM, Dmitrii Dimandt <dmitrii at dmitriid.com> wrote:
But you can’t
var x = import
, for example. I guess you can’tvar import = {}
either.Hmmm… I wonder why…
Because import
has been on the list of reserved or future reserved
words since at least ES3 (1999). I didn't go back further than that.
-- T.J. Crowder
I’d recommend you assume your opponent has done some light reading. And I’d suggest you yourself read the links you post (that is, practice what you preach).
Multiple reserved keywords have been both added to the language and removed from the language. Because the language evolves.
However, you (and TC39 in general) keep insisting that short-sightedness and ad-hoc solutions is the only way forward for JavaScript.
You don’t like System, you think it cannot be used? Oh, introduce an introspect
keyword. Introduce a system
keyword. Heck, nothing stopped you from introducing a language-level built-in in the form of Symbol, what’s stopping you now?
On Fri, 04 Aug 2017 at 09:55 Jordan Harband
< mailto:Jordan Harband <ljharb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
Because it's been reserved syntax since JavaScript's inception, and System hasn't.
I'd recommend some light reading before attempting to continue arguing: mathiasbynens.be/notes/reserved-keywords
On Fri, Aug 4, 2017 at 12:53 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
But you can’t var x = import
, for example. I guess you can’t var import = {}
either.
Hmmm… I wonder why…
On Fri, 04 Aug 2017 at 09:50 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
It can't be made syntax, because var System = {};
is valid code, and we can't break the web. (seriously)
On Fri, Aug 4, 2017 at 12:31 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Make “System” syntax, and there you go.
Instead we have multiple ad-hoc random additions to random keywords just because someone needs something and since there are rarely any long-term design decisions anymore, we’re stuck with new.target, function.sent, import.meta (add your own)
Seriously. How is new.target is “syntax that has context information”, but System.whatever cannot be provided with context information because it’s API?
On Fri, 04 Aug 2017 at 09:26 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
Let’s continue with the trend of light reading. Let’s see the multitude of things that are in JS, and no one bats an eye:
— start quote —
Note that
implements
,
let
,
private
,
public
,
interface
,
package
,
protected
,
static
, and
yield
are disallowed in strict mode only.
You may have noticed I included
eval
and
arguments
in the list. These are not strictly reserved words, but they sure
ecma-international.org/ecma-262/5.1/#sec-12.2.1 — they’re disallowed in strict mode too.
Also, the (unlisted)
NaN
,
Infinity
, and
undefined
properties of the global object are immutable or read-only properties in ES5. So even though
var NaN = 42;
in the global scope wouldn’t throw an error, it wouldn’t actually do anything. To avoid confusion, I’d suggest avoiding the use of these identifiers altogether, even though they’re not strictly reserved words.
— end quote —
Oh, hello. What do we have here? Non-reserved words like they are reserved, and JS treating them in a special way.
So. The only reason not to introduce a proper global introspection API is?
On Fri, 04 Aug 2017 at 10:03 dmitrii at dmitriid.com < mailto:dmitrii at dmitriid.com
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
I’d recommend you assume your opponent has done some light reading. And I’d suggest you yourself read the links you post (that is, practice what you preach).
Multiple reserved keywords have been both added to the language and removed from the language. Because the language evolves.
However, you (and TC39 in general) keep insisting that short-sightedness and ad-hoc solutions is the only way forward for JavaScript.
You don’t like System, you think it cannot be used? Oh, introduce an introspect
keyword. Introduce a system
keyword. Heck, nothing stopped you from introducing a language-level built-in in the form of Symbol, what’s stopping you now?
On Fri, 04 Aug 2017 at 09:55 Jordan Harband
< mailto:Jordan Harband <ljharb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
Because it's been reserved syntax since JavaScript's inception, and System hasn't.
I'd recommend some light reading before attempting to continue arguing: mathiasbynens.be/notes/reserved-keywords
On Fri, Aug 4, 2017 at 12:53 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
But you can’t var x = import
, for example. I guess you can’t var import = {}
either.
Hmmm… I wonder why…
On Fri, 04 Aug 2017 at 09:50 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
It can't be made syntax, because var System = {};
is valid code, and we can't break the web. (seriously)
On Fri, Aug 4, 2017 at 12:31 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Make “System” syntax, and there you go.
Instead we have multiple ad-hoc random additions to random keywords just because someone needs something and since there are rarely any long-term design decisions anymore, we’re stuck with new.target, function.sent, import.meta (add your own)
Seriously. How is new.target is “syntax that has context information”, but System.whatever cannot be provided with context information because it’s API?
On Fri, 04 Aug 2017 at 09:26 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
Dmitrii,
Quick aside: the rude manner in which you are communicating is interfering with your goal of convincing anyone. Perhaps if you tried not being so rude, people here would be more willing to listen to what you're saying.
Sorry, it’s just the manner in which Javascript is being actively demolished has really irked me. Especially the speed with which these decisions are carried out. Suggestions like “you should really do some light reading before engaging in arguments” don’t help either.
These are decisions we as developers have to live with for years to come. It’s very easy to add features to a language. It’s almost impossible to remove them. Hence the (uncalled for) curtness.
On Fri, 04 Aug 2017 at 14:31 James M Snell
< mailto:James M Snell <jasnell at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
Dmitrii,
Quick aside: the rude manner in which you are communicating is interfering with your goal of convincing anyone. Perhaps if you tried not being so rude, people here would be more willing to listen to what you're saying.
- James
On Fri, Aug 4, 2017 at 1:09 AM Dmitrii Dimandt < mailto:dmitrii at dmitriid.com
wrote:
Let’s continue with the trend of light reading. Let’s see the multitude of things that are in JS, and no one bats an eye:
— start quote —
Note that
implements
,
let
,
private
,
public
,
interface
,
package
,
protected
,
static
, and
yield
are disallowed in strict mode only.
You may have noticed I included
eval
and
arguments
in the list. These are not strictly reserved words, but they sure
ecma-international.org/ecma-262/5.1/#sec-12.2.1 — they’re disallowed in strict mode too.
Also, the (unlisted)
NaN
,
Infinity
, and
undefined
properties of the global object are immutable or read-only properties in ES5. So even though
var NaN = 42;
in the global scope wouldn’t throw an error, it wouldn’t actually do anything. To avoid confusion, I’d suggest avoiding the use of these identifiers altogether, even though they’re not strictly reserved words.
— end quote —
Oh, hello. What do we have here? Non-reserved words like they are reserved, and JS treating them in a special way.
So. The only reason not to introduce a proper global introspection API is?
On Fri, 04 Aug 2017 at 10:03 mailto:dmitrii at dmitriid.com < mailto:dmitrii at dmitriid.com
wrote:
I’d recommend you assume your opponent has done some light reading. And I’d suggest you yourself read the links you post (that is, practice what you preach).
Multiple reserved keywords have been both added to the language and removed from the language. Because the language evolves.
However, you (and TC39 in general) keep insisting that short-sightedness and ad-hoc solutions is the only way forward for JavaScript.
You don’t like System, you think it cannot be used? Oh, introduce an introspect
keyword. Introduce a system
keyword. Heck, nothing stopped you from introducing a language-level built-in in the form of Symbol, what’s stopping you now?
On Fri, 04 Aug 2017 at 09:55 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
Because it's been reserved syntax since JavaScript's inception, and System hasn't.
I'd recommend some light reading before attempting to continue arguing: mathiasbynens.be/notes/reserved-keywords
On Fri, Aug 4, 2017 at 12:53 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
But you can’t var x = import
, for example. I guess you can’t var import = {}
either.
Hmmm… I wonder why…
On Fri, 04 Aug 2017 at 09:50 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
It can't be made syntax, because var System = {};
is valid code, and we can't break the web. (seriously)
On Fri, Aug 4, 2017 at 12:31 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Make “System” syntax, and there you go.
Instead we have multiple ad-hoc random additions to random keywords just because someone needs something and since there are rarely any long-term design decisions anymore, we’re stuck with new.target, function.sent, import.meta (add your own)
Seriously. How is new.target is “syntax that has context information”, but System.whatever cannot be provided with context information because it’s API?
On Fri, 04 Aug 2017 at 09:26 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
On Fri, Aug 4, 2017 at 1:45 PM, Dmitrii Dimandt <dmitrii at dmitriid.com>
wrote:
...the manner in which Javascript is being actively demolished...
Again: Not remotely constructive, and not likely to advance your goal in any way.
And yes, Jordan's irritated reply could have been better phrased as well.
Can we all please proceed with professionalism, respect for the fact reasonable people can reach different conclusions (your "demolished" is someone else's thoroughly-researched-and-planned proposal), and courtesy, even in the face of those differing conclusions?
-- T.J. Crowder
I don't want to fan a fire and personally I've preferred other solution s for module meta data but nothing is being rushed imo. I've been closely tracking this discussion in tc39 for some years and while again some of the current front runners are not my favorite proposals they are well thought out, well intended, and painstakingly deliberated.
Now is the time to raise objections but please it helps no one's cause to come at this with anything but constructive discourse. You have validity in your objections but it's going to be difficult to Garner support without tact.
I'd like to refocus this thread myself. I have to dig around for the
meeting notes where it was decided that import.meta should proceed over
import meta
but I do remember there was no rationale other than
consensus. Can some of you who physically attend those meetings provide a
real comparison and some of the whys for that consensus?
A different thread should be used to discuss:
-
TC39 process
-
keyword meta properties in general
-
API centric reflection
-
Module Loader features not related to module context specific meta data
-
Matthew Robb
import.meta isn’t rushed. yet. dynamic imports? definitely rushed. That’s the only reason import.meta exists in the first place. (Much like function.sent, new.target and many other things).
Ad-hoc shortsighted solutions lead to ad-hoc shortsighted solutions. Worse still, these solutions have actually been defended by other equally bad solutions!
dynamic import’s looks-like-function-doesn’t-look-like-function was defended by invoking things like !function{}() [1]. import.meta is defended by pointing at new.target (link lost in my memory) etc. Basically these features just open a huge can of worms that will keep spreading uncontrollably throughout the language. It’s already happening and we are already witness to this.
[1] tc39/proposal-dynamic-import#35
P.S. regarding polluting import
, this proposal for node actually takes this into consideration: WebReflection/node-eps/blob/master/XXX-module-import.md#why-polluting-module-and-not-require-
On Fri, 04 Aug 2017 at 14:54 Matthew Robb
< mailto:Matthew Robb <matthewwrobb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
I don't want to fan a fire and personally I've preferred other solution s for module meta data but nothing is being rushed imo. I've been closely tracking this discussion in tc39 for some years and while again some of the current front runners are not my favorite proposals they are well thought out, well intended, and painstakingly deliberated.
Now is the time to raise objections but please it helps no one's cause to come at this with anything but constructive discourse. You have validity in your objections but it's going to be difficult to Garner support without tact.
On Aug 4, 2017 8:45 AM, "Dmitrii Dimandt" < mailto:dmitrii at dmitriid.com
wrote:
Sorry, it’s just the manner in which Javascript is being actively demolished has really irked me. Especially the speed with which these decisions are carried out. Suggestions like “you should really do some light reading before engaging in arguments” don’t help either.
These are decisions we as developers have to live with for years to come. It’s very easy to add features to a language. It’s almost impossible to remove them. Hence the (uncalled for) curtness.
On Fri, 04 Aug 2017 at 14:31 James M Snell
< mailto:James+M+Snell+%3Cjasnell at gmail.com%3E
wrote:
Dmitrii,
Quick aside: the rude manner in which you are communicating is interfering with your goal of convincing anyone. Perhaps if you tried not being so rude, people here would be more willing to listen to what you're saying.
- James
On Fri, Aug 4, 2017 at 1:09 AM Dmitrii Dimandt < mailto:dmitrii at dmitriid.com
wrote:
Let’s continue with the trend of light reading. Let’s see the multitude of things that are in JS, and no one bats an eye:
— start quote —
Note that
implements
,
let
,
private
,
public
,
interface
,
package
,
p
rotected
,
static
, and
yield
are disallowed in strict mode only.
You may have noticed I included
eval
and
arguments
in the list. These are not strictly reserved words, but they sure
ecma-international.org/ecma-262/5.1/#sec-12.2.1 — they’re disallowed in strict mode too.
Also, the (unlisted)
NaN
,
Infinity
, and
undefined
properties of the global object are immutable or read-only properties in ES5. So even though
var NaN = 42;
in the global scope wouldn’t throw an error, it wouldn’t actually do anything. To avoid confusion, I’d suggest avoiding the use of these identifiers altogether, even though they’re not strictly reserved words.
— end quote —
Oh, hello. What do we have here? Non-reserved words like they are reserved, and JS treating them in a special way.
So. The only reason not to introduce a proper global introspection API is?
On Fri, 04 Aug 2017 at 10:03 mailto:dmitrii at dmitriid.com < mailto:dmitrii at dmitriid.com
wrote:
I’d recommend you assume your opponent has done some light reading. And I’d suggest you yourself read the links you post (that is, practice what you preach).
Multiple reserved keywords have been both added to the language and removed from the language. Because the language evolves.
However, you (and TC39 in general) keep insisting that short-sightedness and ad-hoc solutions is the only way forward for JavaScript.
You don’t like System, you think it cannot be used? Oh, introduce an introspect
keyword. Introduce a system
keyword. Heck, nothing stopped you from introducing a language-level built-in in the form of Symbol, what’s stopping you now?
On Fri, 04 Aug 2017 at 09:55 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
Because it's been reserved syntax since JavaScript's inception, and System hasn't.
I'd recommend some light reading before attempting to continue arguing: mathiasbynens.be/notes/reserved-keywords
On Fri, Aug 4, 2017 at 12:53 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
But you can’t var x = import
, for example. I guess you can’t var import = {}
either.
Hmmm… I wonder why…
On Fri, 04 Aug 2017 at 09:50 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
It can't be made syntax, because var System = {};
is valid code, and we can't break the web. (seriously)
On Fri, Aug 4, 2017 at 12:31 AM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Make “System” syntax, and there you go.
Instead we have multiple ad-hoc random additions to random keywords just because someone needs something and since there are rarely any long-term design decisions anymore, we’re stuck with new.target, function.sent, import.meta (add your own)
Seriously. How is new.target is “syntax that has context information”, but System.whatever cannot be provided with context information because it’s API?
On Fri, 04 Aug 2017 at 09:26 Jordan Harband
< mailto:Jordan+Harband+%3Cljharb at gmail.com%3E
wrote:
There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
Just want to add some links to this convo:
tc39/tc39-notes/blob/master/es8/2017-05/may-23.md#16iib-module-import-options-discussion-potentially-for-stage-1, docs.google.com/presentation/d/1p1BGFY05-iCiop8yV0hNyWU41_wlwwfv6HIDkRNNIBQ/edit?usp=sharing
- Matthew Robb
This seems to be the conclusion the lead to consensus:
YK: Option #3.c (import { url } from here) is undesirable because it must
be top-level.
(bikeshed discussion of what to call import.thing) (general consensus for
import.meta)
MM: What about exposing dynamic import() as a method of import.meta?
DH: import.meta.import() is just really verbose for a common idiom.
BF: And import() should be available in script, where the import.meta
meta-property is not available.
Conclusion/Resolution
Let's go with import.meta; Domenic to come back later this meeting with a
proposal
- Matthew Robb
And here is the notes from progressing the proposal to stage 2: tc39/tc39-notes/blob/master/es8/2017-05/may-25.md#15iif-importmeta-for-stage-2
- Matthew Robb
Make “System” syntax, and there you go.
Dmitrii, I like that, let’s continue with that train of thoughts! How do you make System syntax? You can’t consider it an object (it is not an object, it is syntax), which means you can’t pass it around because it holds contextual information (linked to the source text were it was used, similar to our beloved eval), it means you can’t really use other syntax to spread it, because it is not a object, etc, etc, etc. Do you see where I’m going? You quickly ended up with something almost equivalent to import().
We didn’t come up with it in the shower, it took years to understand all the problems, and create consensus, definitely not rushed IMO.
I was going to write a big answer going into details while a system
“special form” would be beneficial, extensible and future-proof, draw comparisons and parallels to super()
etc.
Instead I will leave you with this quote and links from node.js enhancement proposals on module.import
WebReflection/node-eps/blob/master/XXX-module-import.md:
— start quote —
Why polluting module and not require ?
require function has been historically abused (www.npmjs.com/package/require.async) or polluted (requirejs.org/docs/api.html#data-main) in an era where Promise, as well as async and await patterns, weren't even discussed in the JS community.
...
On the other hand, being module where developers define their exports, I don't see any confusion in being module where developers can import their dependencies.
— end quote —
It’s funny to see how TC39 ended up abusing and polluting import
.
On Fri, 04 Aug 2017 at 16:58 "Caridy Patiño"
< mailto:
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
Make “System” syntax, and there you go.
Dmitrii, I like that, let’s continue with that train of thoughts! How do you make System syntax? You can’t consider it an object (it is not an object, it is syntax), which means you can’t pass it around because it holds contextual information (linked to the source text were it was used, similar to our beloved eval), it means you can’t really use other syntax to spread it, because it is not a object, etc, etc, etc. Do you see where I’m going? You quickly ended up with something almost equivalent to import().
We didn’t come up with it in the shower, it took years to understand all the problems, and create consensus, definitely not rushed IMO.
/caridy
On Aug 4, 2017, at 3:31 AM, Dmitrii Dimandt < mailto:dmitrii at dmitriid.com
wrote:
Make “System” syntax, and there you go.
Instead we have multiple ad-hoc random additions to random keywords just because someone needs something and since there are rarely any long-term design decisions anymore, we’re stuck with new.target, function.sent, import.meta (add your own)
Seriously. How is new.target is “syntax that has context information”, but System.whatever cannot be provided with context information because it’s API?
On Fri, 04 Aug 2017 at 09:26 Jordan Harband
<> wrote:
There’s nothing stopping you from providing context to System.load. Or Loader.import, or…
Those are APIs. It is, in fact, impossible to provide context with API, since it's just normal functions - it must be with syntax.
Additionally, please don't use sexual language, especially in a derogatory manner - that's against TC39's code of conduct, and I'm quite sure it won't be tolerated on this list.
Criticism that's purely insult, and doesn't actually explain the cons of something, is also not productive or useful.
I would be comfortable with module.import()
and module.meta
instead of
import()
and import.meta
, simply because the name module
more
automatically describes the module specific "global" object than import
does. Also, it more explicitly communicates that the "import" is specific
to the module and hence how it is able to use its folder path etc. as root,
for example. I would be comfortable if everything else about them stayed
the same as the proposals though
I would like to see the module
object and module.import()
included in
the "Alternative solutions explored" section in the dynamic imports
proposal: tc39/proposal-dynamic-import along with the
explanation that module
could be the module specific "global" object
instead of import
, along with module.meta
as an alternative to
tc39/proposal-import-meta . I think it is the most
compelling of the "alternative solutions".
On Sat, Aug 5, 2017 at 10:13 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:
I would be comfortable with
module.import()
andmodule.meta
instead ofimport()
andimport.meta
, simply because the namemodule
more automatically describes the module specific "global" object thanimport
does.
If module
were used, it would have to be an identifier (like Symbol
and
Reflect
), not a keyword; we can't break existing code using module
as
an identifier. And like Symbol
and Reflect
, codebases that use it for
something else would have to adjust if they want to use the new thing. (If
they don't need to use the new thing, they're fine, they just shadow it.)
If it's an identifier, it contains a value; that value can be assigned to other variables, passed into functions, etc. I'm not steeped enough in the proposal to know whether that's a problem; I suspect it is. But I thought I'd note it.
-- T.J. Crowder
Does existing code use modules??? My understanding is that modules are a
recent concept. And the module
keyword would only apply in actual modules
Pls correct me if I'm wrong
On Sat, Aug 5, 2017 at 11:05 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:
Does existing code use modules??? My understanding is that modules are a recent concept.
Yes, people have been using ES2015-style modules for a couple of years now (obviously, modules in general much longer than that), via transpilers and bundlers, and recently with some initial native implementations (Chrome v60 has it behind a flag, I think v61 will have it by default; I believe there's a Safari tech preview). But I mostly meant code in general (and/or which is bundled up in a module at some point), not module-specific code.
And the
module
keyword would only apply in actual modules
I imagine that's possible. In general, something that's an identifier in
some code but a keyword in other code doesn't sound like a good idea to me.
But there's something like a precedent: Until ES5, you couldn't use a
keyword as a literal property name (e.g., in a property accessor --
element.class
and element.for
were famously syntax errors, leading to
the DOM using className
and htmlFor
instead); as of ES5, you can,
because the context is sufficiently clear that the accessor and object
initializer productions could use IdentifierName rather than
Identifier. Your concept goes further, making the same production parse
as a keyword in ModuleBody code but an identifier in ScriptBody code.
These issues don't exist with import
, which has been a "future" reserved
word for 20 years. So while module
may be slightly more semantically
obvious, I think you'll struggle to get people on-board with the complexity
of adding a context-sensitive keyword.
-- T.J. Crowder
On Sat, Aug 5, 2017 at 11:45 AM, T.J. Crowder <tj.crowder at farsightsoftware.com> wrote:
...But there's something like a precedent...
Also set
and get
in object initializers and classes. (Apologies for
double-posting.)
-- T.J. Crowder
How is document
and window
handled when someone does const document =
? It would seem perfectly fine to allow module
to be masked by other
variables, and if someone wants to use the module-global module
, they can
just rename in order to get access. It is analogous to document
/
window
after all. Right?
On Sat, Aug 5, 2017 at 11:58 AM, Naveen Chawla <naveen.chwl at gmail.com> wrote:
How is
document
andwindow
handled when someone doesconst document =
?
At global scope in a script body, in a browser context, it's an error,
as you presumably know, because document
is already declared.
It would seem perfectly fine to allow
module
to be masked by other variables, and if someone wants to use the module-globalmodule
, they can just rename in order to get access.
Yes. That's what I said.
The issue with it being an identifier isn't shadowing. It's that then it's a binding with a value, and that value can be passed around, which I suspect isn't okay.
For clarity: To get module
, either:
-
It's a context-sensitive keyword, and code that's using it needs to be updated when migrated to a module.
-
It's an identifier, which means its value can be passed around.
All I've said, again, is: I suspect that having it be an identifier is a non-starter. But perhaps you can get support for a context-sensitive keyword, if people feel it's worth the complexity for mildly-improved semantics.
-- T.J. Crowder
I think it has to be an identifier, especially given proposals like meta
,
dynamic functionality etc. The question for me is whether it's import
or
module
. Suppose it's import
: already proposed to act as a per-module
function (import()
) with a property (import.meta
) are we trying to
prevent things like:
import.myModuleVar = 5
and console.log('Import is '+import);
?
Passing it around seems fine to me. What if you want to aggregate your modules into a list and see what's going on?
I see import
or module
as analogous to window
but per-module.
I'd like to hear why passing import
or module
around should be
expressly disallowed.
Both import
and module
alternatives are backwards-compatible /
existing-code friendly as an identifier, by allowing masking and/or
reassignment with no effect. For me the question is which offers better
clarity and less confusion.
I'd love to be corrected if wrong
On Sat, Aug 5, 2017 at 1:46 PM, Naveen Chawla <naveen.chwl at gmail.com> wrote:
I think it has to be an identifier, especially given proposals like
meta
, dynamic functionality etc.
There's a slight misunderstanding here about the term "identifier" that may
be preventing your fully understanding what I'm saying. import
isn't, and
cannot be, an identifier, because it's a keyword. See the Identifiers
section of the spec. This is the fundamental difference between
import
and document
(or perhaps more properly [since document
isn't a
JavaScript thing], between import
and, say, undefined
or Symbol
).
Understanding the difference between an identifier and a keyword may help
you understand better what I'm saying regarding why module
as an
identifier might be a problem.
I'll leave the question of whether passing that identifier's value around would be a problem to people more familiar with the complexities of the import mechanisms. As I've said several times now: I suspect it's a problem, I don't know that it is.
-- T.J. Crowder
Thanks for the link! That means that import
is already on the borderline
of the spec since it wants to be a function and object. module
would
eliminate all confusion and restriction but as you said, only if passing it
around must be expressly disallowed - need an interested TC39 member to
weigh in
On Sat, Aug 5, 2017 at 2:35 PM, Naveen Chawla <naveen.chwl at gmail.com> wrote:
Thanks for the link! That means that
import
is already on the
borderline of the spec since it wants to be a function and object.
No, not at all. It's a keyword. import.meta
doesn't make import
an
object, any more than new.target
makes new
an object.
-- T.J. Crowder
Yes although it could be implemented like an object & function underneath even if it's not officially exposed as one.
I think the key question is for interested TC39 members - whether passing
it around must be expressly disallowed or allowed. If allowed, module
is
the only choice that won't go against the existing advice against
identifiers being keywords (besides being a more suitable name anyway). If
it must be expressly disallowed, import
would be the compromise choice.
A use-case for passing it somewhere might be to a static helper function
which lives in another module, and which might use the meta
information,
and which carries out the import whose parameter is based on some logic
that is repeated throughout the app.
Reasons for disallowing this must come from TC39 - till then I'm stumped
It all boils down to direct and indirect evaluation and static contextual information. It can't be an identifier any more than super or this. It can't be passed because it's a different type of evaluation that happens relative to static context, in this case the containing module body.
You can pass access to it by closing over it however.
On Aug 5, 2017 11:47 AM, "Naveen Chawla" <naveen.chwl at gmail.com> wrote:
Yes although it could be implemented like an object & function underneath even if it's not officially exposed as one.
I think the key question is for interested TC39 members - whether passing
it around must be expressly disallowed or allowed. If allowed, module
is
the only choice that won't go against the existing advice against
identifiers being keywords (besides being a more suitable name anyway). If
it must be expressly disallowed, import
would be the compromise choice.
A use-case for passing it somewhere might be to a static helper function
which lives in another module, and which might use the meta
information,
and which carries out the import whose parameter is based on some logic
that is repeated throughout the app.
Reasons for disallowing this must come from TC39 - till then I'm stumped
Exactly! import.meta doesn’t make import an object. new.target doesn’t make new an object. function.sent doesn’t make function an object.
These are just purely arbitrary things tacked on top of randomly selected keywords because at one point someone needed some introspection info (such as “current execution context” etc.). Instead of designing a proper introspection API (or even the beginnings of it), we now have:
-
keywords that are just keywords, really (typeof, case, break, etc.)
-
keywords that are just keywords, but don’t even exist in a language. They are reserved for future use in various contexts: always reserved, only in strict mode, only in module code etc. (enum, public, private, await etc.). May never be used and may possibly be removed, as some keywords have been (int, byte, char etc.)
-
literals that are basically keywords (null, true, false)
-
non-keywords that are for all intents and purposes keywords (eval, arguments)
-
keywords that look like objects (because they have additional properties) which are not objects (new with new.target)
-
keywords that look like functions (because they are invoked like functions and return values like functions) which are not functions (import)
- keywords that look like objects and functions but are neither (import)
The last three are now the current fashionable trend in TC39 for some reason. Why?
On Sat, 05 Aug 2017 at 15:40 "T.J. Crowder"
< mailto:
wrote:
On Sat, Aug 5, 2017 at 2:35 PM, Naveen Chawla < mailto:naveen.chwl at gmail.com
wrote:
Thanks for the link! That means that
import
is already on the borderline of the spec since it wants to be a function and object.
No, not at all. It's a keyword. import.meta
doesn't make import
an object, any more than new.target
makes new
an object.
-- T.J. Crowder
Import is already made to be a context-sensitive keyword
I don’t think you can have a
function x() {
import {x} from ‘module’;
}
On Sat, 05 Aug 2017 at 13:07 "T.J. Crowder"
< mailto:
wrote:
On Sat, Aug 5, 2017 at 11:58 AM, Naveen Chawla
< mailto:naveen.chwl at gmail.com
wrote:
How is
document
andwindow
handled when someone does
const document =
?
At global scope in a script body, in a browser context, it's an error,
as you presumably know, because document
is already declared.
It would seem perfectly fine to allow
module
to be masked by
other variables, and if someone wants to use the module-global
module
, they can just rename in order to get access.
Yes. That's what I said.
The issue with it being an identifier isn't shadowing. It's that then
it's a binding with a value, and that value can be passed around,
which I suspect isn't okay.
For clarity: To get module
, either:
- It's a context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module.
- It's an identifier, which means its value can be passed around.
All I've said, again, is: I suspect that having it be an identifier
is a non-starter. But perhaps you can get support for a
context-sensitive keyword, if people feel it's worth the complexity
for mildly-improved semantics.
-- T.J. Crowder
Can anyone knowledgeable on the topic of direct vs indirect evaluation chime in with an explanation or a link to one as my understanding is enough to "get it" but not too really explain it.
Ah but you can do:
export function meta(key) { return import.meta[key]: }
Languages evolve. It means that at some point something will be introduced that may break existing user code.
I understand that with JS the problem is compounded a billion-fold (JS is used quite literally everywhere).
However: Looking at the evolution of JS we can see that new entities and new assumptions (potentially breaking user code) are continuously added to and removed from JS (as they should).
So, in my opinion, the argument for not adding new global entities such as System, or Module, or Loader (or heck, even all three of them) being “these are not keywords, we can’t introduce them” is really really weak.
On Sat, 05 Aug 2017 at 12:45 "T.J. Crowder"
< mailto:
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
On Sat, Aug 5, 2017 at 11:05 AM, Naveen Chawla
< mailto:naveen.chwl at gmail.com
wrote:
Does existing code use modules??? My understanding is that
modules are a recent concept.
Yes, people have been using ES2015-style modules for a couple of years now (obviously, modules in general much longer than that), via transpilers and bundlers, and recently with some initial native implementations (Chrome v60 has it behind a flag, I think v61 will have it by default; I believe there's a Safari tech preview). But I mostly meant code in general (and/or which is bundled up in a module at some point), not module-specific code.
And the
module
keyword would only apply in actual modules
I imagine that's possible. In general, something that's an identifier in some code but a keyword in other code doesn't sound like a good idea to me. But there's something like a precedent: Until ES5, you couldn't use a keyword as a literal property name (e.g., in a property accessor -- element.class
and element.for
were famously syntax errors, leading to the DOM using className
and htmlFor
instead); as of ES5, you can, because the context is sufficiently clear that the accessor and object initializer productions could use IdentifierName rather than Identifier. Your concept goes further, making the same production parse as a keyword in ModuleBody code but an identifier in ScriptBody code.
These issues don't exist with import
, which has been a "future" reserved word for 20 years. So while module
may be slightly more semantically obvious, I think you'll struggle to get people on-board with the complexity of adding a context-sensitive keyword.
-- T.J. Crowder
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-sensitive keyword (as are a bunch of others, like super. Is super a keyword BTW?)
On Sat, 05 Aug 2017 at 18:03 Matthew Robb
< mailto:Matthew Robb <matthewwrobb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
Ah but you can do:
export function meta(key) {
return import.meta[key]:
}
On Aug 5, 2017 11:59 AM, "Dmitrii Dimandt" < mailto:dmitrii at dmitriid.com
wrote:
Import is already made to be a context-sensitive keyword
I don’t think you can have a
function x() {
import {x} from ‘module’;
}
On Sat, 05 Aug 2017 at 13:07 "T.J. Crowder"
<
">"T.J. Crowder"
wrote:
On Sat, Aug 5, 2017 at 11:58 AM, Naveen Chawla
< mailto:naveen.chwl at gmail.com
wrote:
How is
document
andwindow
handled when someone does
const document =
?
At global scope in a script body, in a browser context, it's an error,
as you presumably know, because document
is already declared.
It would seem perfectly fine to allow
module
to be masked by
other variables, and if someone wants to use the module-global
module
, they can just rename in order to get access.
Yes. That's what I said.
The issue with it being an identifier isn't shadowing. It's that then
it's a binding with a value, and that value can be passed around,
which I suspect isn't okay.
For clarity: To get module
, either:
- It's a context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module.
- It's an identifier, which means its value can be passed around.
All I've said, again, is: I suspect that having it be an identifier
is a non-starter. But perhaps you can get support for a
context-sensitive keyword, if people feel it's worth the complexity
for mildly-improved semantics.
-- T.J. Crowder
Yes super is a keyword and had been reserved for a long time. I don't necessarily disagree with your premise about introducing new keywords that aren't reserved but the current tc39 policy on this is to not due to the high likelihood that it breaks current user code.
This conversation could go very different if that policy were amended and if you think you have a way to do this that doesn't break existing user code I would suggest submitting that as a proposal.
Introduced in ECMASCRIPT 2015: Reflect is a built-in object that provides methods for interceptable JavaScript operations.
Introduced in ECMASCRIPT 2015: The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax "new Symbol()”.
ECMAScript 5 introduced: yield, let
ECMASCRIPT 6 introduced: await as a reserved word in module code
So, introducting new things into the language is not really such a big problem as it’s made out to be ;)
What TC39 has opted to do though is this (quoting myself):
-
keywords that look like objects (because they have additional properties) which are not objects (new with new.target)
-
keywords that look like functions (because they are invoked like functions and return values like functions) which are not functions (import)
-
keywords that look like objects and functions but are neither (import)
I’ll add another one: overloading of existing keywords with new meanings, semantics and behaviour in different contexts.
The proposal thus is an easy one: stop doing that :) Move system-related functionality to System. Make it special. Move module-related functionality to Module. Move introspection-related functionality to Introspect. Easy-peasy ;)
On Sat, 05 Aug 2017 at 18:14 Matthew Robb
< mailto:Matthew Robb <matthewwrobb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
Yes super is a keyword and had been reserved for a long time. I don't necessarily disagree with your premise about introducing new keywords that aren't reserved but the current tc39 policy on this is to not due to the high likelihood that it breaks current user code.
This conversation could go very different if that policy were amended and if you think you have a way to do this that doesn't break existing user code I would suggest submitting that as a proposal.
On Aug 5, 2017 12:08 PM, "Dmitrii Dimandt" < mailto:dmitrii at dmitriid.com
wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-sensitive keyword (as are a bunch of others, like super. Is super a keyword BTW?)
On Sat, 05 Aug 2017 at 18:03 Matthew Robb
< mailto:Matthew+Robb+%3Cmatthewwrobb at gmail.com%3E
wrote:
Ah but you can do:
export function meta(key) {
return import.meta[key]:
}
On Aug 5, 2017 11:59 AM, "Dmitrii Dimandt" < mailto:dmitrii at dmitriid.com
wrote:
Import is already made to be a context-sensitive keyword
I don’t think you can have a
function x() {
import {x} from ‘module’;
}
On Sat, 05 Aug 2017 at 13:07 "T.J. Crowder"
<
">"T.J. Crowder"
wrote:
On Sat, Aug 5, 2017 at 11:58 AM, Naveen Chawla
< mailto:naveen.chwl at gmail.com
wrote:
How is
document
andwindow
handled when someone does
const document =
?
At global scope in a script body, in a browser context, it's an error,
as you presumably know, because document
is already declared.
It would seem perfectly fine to allow
module
to be masked by
other variables, and if someone wants to use the module-global
module
, they can just rename in order to get access.
Yes. That's what I said.
The issue with it being an identifier isn't shadowing. It's that then
it's a binding with a value, and that value can be passed around,
which I suspect isn't okay.
For clarity: To get module
, either:
- It's a context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module.
- It's an identifier, which means its value can be passed around.
All I've said, again, is: I suspect that having it be an identifier
is a non-starter. But perhaps you can get support for a
context-sensitive keyword, if people feel it's worth the complexity
for mildly-improved semantics.
-- T.J. Crowder
On Sat, Aug 5, 2017 at 5:05 PM, Dmitrii Dimandt <dmitrii at dmitriid.com> wrote:
So, in my opinion, the argument for not adding new global entities such as System, or Module, or Loader (or heck, even all three of them) being “these are not keywords, we can’t introduce them” is really really weak.
Is anyone making that argument? I certainly am not. Not only is it possible
to add more global entities, as you point out, it's been done repeatedly:
Symbol
, Reflect
, etc. They just can't be keywords without breaking
things. They have to be identifiers. Which means they have bindings with
values. Which means those values can be copied. Which has implications.
On Sat, Aug 5, 2017 at 5:08 PM, Dmitrii Dimandt <dmitrii at dmitriid.com> wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a context-sensitive keyword, and code that's using it needs to be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context- sensitive keyword (as are a bunch of others, like super. Is super a keyword BTW?)
My point was that this would be the only case I know of where it would be a
keyword in one context but an identifier in another in the exact same
production. super
, import
, etc., are always keywords. You just
can't use them except in certain contexts. So I shouldn't have said
"context-sensitive keyword" so much as "keyword or identifier depending on
context." (But then...I did, earlier; I figured the shorthand was okay
after spelling it out longhand. :-) )
But again: Maybe that's feasible. Or maybe it's not a problem passing the
value around, in which case a predefined module
identifier only in module
code isn't a problem anyway.
-- T.J. Crowder
Too bad emails don’t have "thumbs up" and “+1”s :) So here’s my "+1” to you
On Sat, 05 Aug 2017 at 18:28 "T.J. Crowder"
< mailto:
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
On Sat, Aug 5, 2017 at 5:05 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
So, in my opinion, the argument for not adding new global entities
such as System, or Module, or Loader (or heck, even all three of
them) being “these are not keywords, we can’t introduce them” is
really really weak.
Is anyone making that argument? I certainly am not. Not only is it possible to add more global entities, as you point out, it's been done repeatedly: Symbol
, Reflect
, etc. They just can't be keywords without breaking things. They have to be identifiers. Which means they have bindings with values. Which means those values can be copied. Which has implications.
On Sat, Aug 5, 2017 at 5:08 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a
context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-
sensitive keyword (as are a bunch of others, like super.
Is super a keyword BTW?)
My point was that this would be the only case I know of where it would be a keyword in one context but an identifier in another in the exact same production. super
, import
, etc., are always keywords. You just can't use them except in certain contexts. So I shouldn't have said "context-sensitive keyword" so much as "keyword or identifier depending on context." (But then...I did, earlier; I figured the shorthand was okay after spelling it out longhand. :-) )
But again: Maybe that's feasible. Or maybe it's not a problem passing the value around, in which case a predefined module
identifier only in module code isn't a problem anyway.
-- T.J. Crowder
Neither should emails have that. Please refer to [1] in the future.
Thank you!
I really can't find a good resource on direct vs indirect evaluation but my understanding is it's one of the main considerations for using a keyword over an identifier for contextual information. One example which is already in the language would be 'eval' which you can read a little about here: 2ality.com/2014/01/eval.html
Now you might be able to have an API that gets you the same result as the context sensitive keywords but it would be less ergonomic among other things: Reflect.getModuleMetaProperty(someModuleNs, 'propName') but this becomes much more difficult to do FROM WITHIN THE MODULE ITSELF. Anything that is, let's call it tangible, cannot receive implicit contextual information it must have something passed to it that it would use to look up said information.
Sure there could be arguments made about introducing new environment type
records to the top level module scope of all modules but this is
potentially much more error prone and likely to lead to more and bigger
questions down the road. 'module' in particular is a really bad choice imo
as node/commonjs have already introduced a 'module' identifier into all of
their module scopes hence module.exports = ...
. There may be solutions to
working around that in one form or another BUT the 'trend' in TC39 to use
keyword meta properties for context sensitive information is to avoid
solving ever edge case of conflict that would impact existing code and
users. It really is a fairly ripe space for powerful and ergonomic features
like super
which feel like "magic". The same is true for import.meta but
it may be harder to identify right off as the uses haven't all been fully
introduced such as environment specific properties and potentialy other
loader hooks.
NOW as I was writing this it came to mind that we DO have a new syntactic form for private data coming in the form of private fields which use a hash prefix. It would be interesting to explore using the same syntax for module scoped private fields:
console.log(#dirname);
- Matthew Robb
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
See my previous post about the multitude of keyword and keyword-like types that the language has. It’s so bad that even “metaproperty” concept itself isn’t defined in the standard except as a hardcoded new.target
[1]
If you read, for example, through the import.meta draft, you see things ripe for inclusion in a proper API object.
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language: new
is an operator[2], it gets new.target
. A function is a callable object [3], and it gets a function.sent
. Import is … I don’t know what import is [4]. It gets transformed into a separate, made-just-for-import CallExpression[5] and then it gets an import.meta
on top of that [6] (as a hardcoded “metaproperty").
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
All this is chaos from the perspective of developer experience. And truly looks like random ad-hoc solutions to immediate problems with no long-term goals. Imagine how much better and future-proof it would be if all this was in the form of a unified centralised API? There is a reason people laugh at PHP for its API and language design.
[1] www.ecma-international.org/ecma-262/#sec-meta-properties
[2] www.ecma-international.org/ecma-262/7.0/#sec-new-operator
[3] www.ecma-international.org/ecma-262/7.0/#sec-terms-and-definitions-function
[4] www.ecma-international.org/ecma-262/7.0/#sec-imports
[5] tc39.github.io/proposal-dynamic-import/#sec-left-hand-side-expressions
[6] tc39.github.io/proposal-import-meta/#sec-left-hand-side-expressions
[7] www.ecma-international.org/ecma-262/7.0/#sec-left-hand-side-expressions
On Sat, 05 Aug 2017 at 18:59 Matthew Robb
< mailto:Matthew Robb <matthewwrobb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
I really can't find a good resource on direct vs indirect evaluation but my understanding is it's one of the main considerations for using a keyword over an identifier for contextual information. One example which is already in the language would be 'eval' which you can read a little about here: 2ality.com/2014/01/eval.html
Now you might be able to have an API that gets you the same result as the context sensitive keywords but it would be less ergonomic among other things: Reflect.getModuleMetaProperty(someModuleNs, 'propName') but this becomes much more difficult to do FROM WITHIN THE MODULE ITSELF. Anything that is, let's call it tangible, cannot receive implicit contextual information it must have something passed to it that it would use to look up said information.
Sure there could be arguments made about introducing new environment type records to the top level module scope of all modules but this is potentially much more error prone and likely to lead to more and bigger questions down the road. 'module' in particular is a really bad choice imo as node/commonjs have already introduced a 'module' identifier into all of their module scopes hence module.exports = ...
. There may be solutions to working around that in one form or another BUT the 'trend' in TC39 to use keyword meta properties for context sensitive information is to avoid solving ever edge case of conflict that would impact existing code and users. It really is a fairly ripe space for powerful and ergonomic features like super
which feel like "magic". The same is true for import.meta but it may be harder to identify right off as the uses haven't all been fully introduced such as environment specific properties and potentialy other loader hooks.
NOW as I was writing this it came to mind that we DO have a new syntactic form for private data coming in the form of private fields which use a hash prefix. It would be interesting to explore using the same syntax for module scoped private fields:
console.log(#dirname);
- Matthew Robb
On Sat, Aug 5, 2017 at 12:35 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Too bad emails don’t have "thumbs up" and “+1”s :) So here’s my "+1” to you
On Sat, 05 Aug 2017 at 18:28 "T.J. Crowder"
<
">"T.J. Crowder"
wrote:
On Sat, Aug 5, 2017 at 5:05 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
So, in my opinion, the argument for not adding new global entities
such as System, or Module, or Loader (or heck, even all three of
them) being “these are not keywords, we can’t introduce them” is
really really weak.
Is anyone making that argument? I certainly am not. Not only is it possible to add more global entities, as you point out, it's been done repeatedly: Symbol
, Reflect
, etc. They just can't be keywords without breaking things. They have to be identifiers. Which means they have bindings with values. Which means those values can be copied. Which has implications.
On Sat, Aug 5, 2017 at 5:08 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a
context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-
sensitive keyword (as are a bunch of others, like super.
Is super a keyword BTW?)
My point was that this would be the only case I know of where it would be a keyword in one context but an identifier in another in the exact same production. super
, import
, etc., are always keywords. You just can't use them except in certain contexts. So I shouldn't have said "context-sensitive keyword" so much as "keyword or identifier depending on context." (But then...I did, earlier; I figured the shorthand was okay after spelling it out longhand. :-) )
But again: Maybe that's feasible. Or maybe it's not a problem passing the value around, in which case a predefined module
identifier only in module code isn't a problem anyway.
-- T.J. Crowder
I just realised that there is also the argument that “global object cannot get current context” and other limitations applied to whether a theoretical “System/Module/Loader/Introspect” would be a global module, or object, or keyword, or any (potentially context-sensitive) combination of these.
However, this all basically depends on what you specify in the standard, doesn’t it? :)
-
Dynamic import has a “forbidden extensions” section[1] and how it should work when it’s invoked as a CallExpression [2]
-
import.meta has a full section describing how the runtime should behave when encountering this particular property[3]
-
new global objects like Reflect, Proxy, Symbol have specifications on what they are and hoe they should be treated [4]
A theoretical global object/keyword/identifier/special form X could be specified as <object/keyword/identifier/avocado>. X.someProperty: when encountered, let context be Ctx, let A be B, and C be B, populate with properties from here and there and everywhere.
Or look at the AwaitExpression[5]. There are specific limits in place to guard where and how it’s used and when it is to be evaluated as AwaitExpression.
So there’s really nothing stopping you from designing a proper System/Module/Loader/Introspect/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
Like look. function.sent?? Really? And it’s extremely highly context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.” [6]
Look. Here’s a proposal: Introspect.context.sent can appear anywhere a YieldExpress would be legal. Referencing Introspect.context.sent outside of a GeneratorBody is a Syntax Error.
And then you can use Introspect.context.target
instead of new.target
. Introspect.context.module
instead of import.meta
. Clean. Reasonable. Extensible. Future-proof.
[1] tc39.github.io/proposal-dynamic-import/#sec-forbidden-extensions
[2] tc39.github.io/proposal-dynamic-import/#sec-import-calls
[3] tc39.github.io/proposal-import-meta/#sec-source-text-module-records
[4] tc39.github.io/ecma262/#sec-reflection, tc39.github.io/ecma262/#sec-proxy-objects, tc39.github.io/ecma262/#sec-symbol-objects
[5] tc39.github.io/ecma262/#prod-AwaitExpression
[6] allenwb/ESideas/blob/master/Generator metaproperty.md
On Sat, 05 Aug 2017 at 20:40 dmitrii at dmitriid.com < mailto:dmitrii at dmitriid.com
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
See my previous post about the multitude of keyword and keyword-like types that the language has. It’s so bad that even “metaproperty” concept itself isn’t defined in the standard except as a hardcoded new.target
[1]
If you read, for example, through the import.meta draft, you see things ripe for inclusion in a proper API object.
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language: new
is an operator[2], it gets new.target
. A function is a callable object [3], and it gets a function.sent
. Import is … I don’t know what import is [4]. It gets transformed into a separate, made-just-for-import CallExpression[5] and then it gets an import.meta
on top of that [6] (as a hardcoded “metaproperty").
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
All this is chaos from the perspective of developer experience. And truly looks like random ad-hoc solutions to immediate problems with no long-term goals. Imagine how much better and future-proof it would be if all this was in the form of a unified centralised API? There is a reason people laugh at PHP for its API and language design.
[1] www.ecma-international.org/ecma-262/#sec-meta-properties
[2] www.ecma-international.org/ecma-262/7.0/#sec-new-operator
[3] www.ecma-international.org/ecma-262/7.0/#sec-terms-and-definitions-function
[4] www.ecma-international.org/ecma-262/7.0/#sec-imports
[5] tc39.github.io/proposal-dynamic-import/#sec-left-hand-side-expressions
[6] tc39.github.io/proposal-import-meta/#sec-left-hand-side-expressions
[7] www.ecma-international.org/ecma-262/7.0/#sec-left-hand-side-expressions
On Sat, 05 Aug 2017 at 18:59 Matthew Robb
< mailto:Matthew Robb <matthewwrobb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
I really can't find a good resource on direct vs indirect evaluation but my understanding is it's one of the main considerations for using a keyword over an identifier for contextual information. One example which is already in the language would be 'eval' which you can read a little about here: 2ality.com/2014/01/eval.html
Now you might be able to have an API that gets you the same result as the context sensitive keywords but it would be less ergonomic among other things: Reflect.getModuleMetaProperty(someModuleNs, 'propName') but this becomes much more difficult to do FROM WITHIN THE MODULE ITSELF. Anything that is, let's call it tangible, cannot receive implicit contextual information it must have something passed to it that it would use to look up said information.
Sure there could be arguments made about introducing new environment type records to the top level module scope of all modules but this is potentially much more error prone and likely to lead to more and bigger questions down the road. 'module' in particular is a really bad choice imo as node/commonjs have already introduced a 'module' identifier into all of their module scopes hence module.exports = ...
. There may be solutions to working around that in one form or another BUT the 'trend' in TC39 to use keyword meta properties for context sensitive information is to avoid solving ever edge case of conflict that would impact existing code and users. It really is a fairly ripe space for powerful and ergonomic features like super
which feel like "magic". The same is true for import.meta but it may be harder to identify right off as the uses haven't all been fully introduced such as environment specific properties and potentialy other loader hooks.
NOW as I was writing this it came to mind that we DO have a new syntactic form for private data coming in the form of private fields which use a hash prefix. It would be interesting to explore using the same syntax for module scoped private fields:
console.log(#dirname);
- Matthew Robb
On Sat, Aug 5, 2017 at 12:35 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Too bad emails don’t have "thumbs up" and “+1”s :) So here’s my "+1” to you
On Sat, 05 Aug 2017 at 18:28 "T.J. Crowder"
<
">"T.J. Crowder"
wrote:
On Sat, Aug 5, 2017 at 5:05 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
So, in my opinion, the argument for not adding new global entities
such as System, or Module, or Loader (or heck, even all three of
them) being “these are not keywords, we can’t introduce them” is
really really weak.
Is anyone making that argument? I certainly am not. Not only is it possible to add more global entities, as you point out, it's been done repeatedly: Symbol
, Reflect
, etc. They just can't be keywords without breaking things. They have to be identifiers. Which means they have bindings with values. Which means those values can be copied. Which has implications.
On Sat, Aug 5, 2017 at 5:08 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a
context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-
sensitive keyword (as are a bunch of others, like super.
Is super a keyword BTW?)
My point was that this would be the only case I know of where it would be a keyword in one context but an identifier in another in the exact same production. super
, import
, etc., are always keywords. You just can't use them except in certain contexts. So I shouldn't have said "context-sensitive keyword" so much as "keyword or identifier depending on context." (But then...I did, earlier; I figured the shorthand was okay after spelling it out longhand. :-) )
But again: Maybe that's feasible. Or maybe it's not a problem passing the value around, in which case a predefined module
identifier only in module code isn't a problem anyway.
-- T.J. Crowder
await
could be added because there is no case in existing code where
await <expression>
would have been valid code, so it is
backward-compatible. The same applies for all of the meta-property
proposals. The same is not true for Introspect.context.module
. It's not
just a question of it a given construct could be made to behave the way you
want, it's also a question of it it would be compatible with existing code.
Introduced in ECMASCRIPT 2015: Reflect is a built-in object that provides
methods for interceptable JavaScript operations.
Not a breaking change because adding a new global isn't new syntax, it's just a new variable that exists and essentially can't break much.
Introduced in ECMASCRIPT 2015: The Symbol() function returns a value of
type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax "new Symbol()”.
Not a breaking change because this constructor did not exist before.
ECMAScript 5 introduced: yield, let
Not a breaking change because those two can only occur in locations where
they can't conflict with existing ES5 code. The extremely limited cases
where let
could be in conflict are explicitly handled in the grammar to
prevent breaking changes.
ECMASCRIPT 6 introduced: await as a reserved word in module code
Same as above.
So, introducting new things into the language is not really such a big
problem as it’s made out to be ;)
No-one said introducing things was not possible, but we can't break existing code.
Don’t forget that
super
gets its own properties. Since there’s no
specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
I don't think I'd call super properties metaproperties for this reason.
super
property access is its own class-related syntax that isn't related. I
do think it sets a perfectly reasonable guideline that makes it clear most
people have no problem accessing properties off of keywords.
The problem with “metaproperties” is that they make the language more
complex, chaotic and make it difficult to reason about the language.
This seems to be the core of your argument, but I honestly don't quite see why it is more complex/chaotic. The biggest issue for me in this whole thread is that it's all extremely opinion-based. What one person calls complex another would call elegant.
What specifically is difficult to reason about that wasn't already complex?
Same for function.arguments
. You're saying you think it's better to have
an automatically-created arguments
variable in every single function
instead of having syntax to access it? arguments
and this
as two
auto-initialized bindings are some of the most confusing parts of JS.
So there’s really nothing stopping you from designing a proper
System/Module/Loader/Introspect/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
I don't think anyone has claimed that import.meta
is meant as a
replacement for these. We still need a loader spec, but having a syntactic
way to access data about the current active module is absolutely a useful
thing to have. It's no different than CommonJS's __dirname and __filename
among others. The logic for implementing a loader is separate from the
logic for defining the behavior of module execution itself.
I'd much rather have a static syntactically-defined way to access that
information over a magically-populated not-quite-global variable like
Introspect.context.sent
.
In a perfect world absolutely module
could have been a keyword, but at
this point making that change seems like an absolute no-go because it could
easily break existing code.
Like look. function.sent?? Really? And it’s extremely highly
context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.”
It's the exact same context-specific behavior as yield
and they are both
tied to generator functions. How is that in any way unexpected?
[Reflect] Not a breaking change because adding a new global isn't new syntax, it's just a new variable that exists and essentially can't break much.
It either isn’t a breaking change, or can’t break much. It cannot be both.
Not a breaking change because this constructor did not exist before.
Are you entirely sure? There’s no code anywhere which doesn’t have an object called Symbol invoked with new Symbol()
? The moment you add a global anything, you break someone’s code somewhere.
I don't think I'd call super properties metaproperties for this reason.
super
property access is its own class-related syntax that isn't related. I do think it sets a perfectly reasonable guideline that makes it clear most people have no problem accessing properties off of keywords.
I fail to see where you see guidelines specced out in the specifications of metaproperties or superproperties. “Most people” end up just stuck with these because they are either unaware that these changes are coming or are not active enough to voice their concerns.
The biggest issue for me in this whole thread is that it's all extremely opinion-based. What one person calls complex another would call elegant.
The thing is, I clearly provide arguments in favour of my opinion. Let me just summarise them for you.
We now have:
-
keywords that are just keywords, really (typeof, case, break, etc.)
-
keywords that are just keywords, but don’t even exist in a language. They are reserved for future use in various contexts: always reserved, only in strict mode, only in module code etc. (enum, public, private, await etc.). May never be used and may possibly be removed, as some keywords have been (int, byte, char etc.)
-
literals that are basically keywords (null, true, false)
-
non-keywords that are for all intents and purposes keywords (eval, arguments)
-
keywords that look like objects (because they have additional properties) which are not objects (new with new.target)
-
keywords that look like functions (because they are invoked like functions and return values like functions) which are not functions (import and import())
-
keywords that look like objects and functions but are neither (import and import() and import.meta)
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language:
-
new
is an operator[2], it getsnew.target
-
A function is a callable object [3], and it gets a
function.sent
-
import is … I don’t know what import is. It gets transformed into a separate, made-just-for-import CallExpression and then it gets an
import.meta
on top of that (as a hardcoded “metaproperty”).
All of the above are basic facts about the language as it exists now. How can adding “properties” to semantically fundamentally different things elegant? Or increasing the number of extremely context-dependent things? Or overriding existing keywords with new and exceedingly confusing behaviours?
So there’s really nothing stopping you from designing a proper System/Module/Loader/Introspect/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
I don't think anyone has claimed that
import.meta
is meant as a replacement for these. We still need a loader spec
and a System spec and an Introspect spec, and ... That is why all these random additions to vastly different things in the language look like ad-hoc shortsighted solutions with no respect for the language or its evolution. “We need a thing, we have nowhere to put this thing, let’s add this thing to a keyword, because new.target (bad design) has opened a door for us”.
I'd much rather have a static syntactically-defined way to access that information over a magically-populated not-quite-global variable like
Introspect.context.sent
.
Riiiight. Because import.meta is not a magically-populated not-quite-global variable. Oh. Wait. That’s exactly what it is.
Even though it’s worse. import is a not-really-a-function-not-really-an-object-not-really-a-static-not-really-a-dynamic-keyword which is clearly far worse than a properly defined API.
On Sat, 05 Aug 2017 at 22:37 Logan Smyth
< mailto:Logan Smyth <loganfsmyth at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
await
could be added because there is no case in existing code where await <expression>
would have been valid code, so it is backward-compatible. The same applies for all of the meta-property proposals. The same is not true for Introspect.context.module
. It's not just a question of it a given construct could be made to behave the way you want, it's also a question of it it would be compatible with existing code.
Introduced in ECMASCRIPT 2015: Reflect is a built-in object that provides methods for interceptable JavaScript operations.
Not a breaking change because adding a new global isn't new syntax, it's just a new variable that exists and essentially can't break much.
Introduced in ECMASCRIPT 2015: The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax "new Symbol()”.
Not a breaking change because this constructor did not exist before.
ECMAScript 5 introduced: yield, let
Not a breaking change because those two can only occur in locations where they can't conflict with existing ES5 code. The extremely limited cases where let
could be in conflict are explicitly handled in the grammar to prevent breaking changes.
ECMASCRIPT 6 introduced: await as a reserved word in module code
Same as above.
So, introducting new things into the language is not really such a big problem as it’s made out to be ;)
No-one said introducing things was not possible, but we can't break existing code.
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
I don't think I'd call super properties metaproperties for this reason. super
property access is its own class-related syntax that isn't related.
I do think it sets a perfectly reasonable guideline that makes it clear most people have no problem accessing properties off of keywords.
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
This seems to be the core of your argument, but I honestly don't quite see why it is more complex/chaotic. The biggest issue for me in this whole thread is that it's all extremely opinion-based. What one person calls complex another would call elegant.
What specifically is difficult to reason about that wasn't already complex? Same for function.arguments
. You're saying you think it's better to have an automatically-created arguments
variable in every single function instead of having syntax to access it? arguments
and this
as two auto-initialized bindings are some of the most confusing parts of JS.
So there’s really nothing stopping you from designing a proper System/Module/Loader/
Introspect/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
I don't think anyone has claimed that import.meta
is meant as a replacement for these. We still need a loader spec, but having a syntactic way to access data about the current active module is absolutely a useful thing to have. It's no different than CommonJS's __dirname and __filename among others. The logic for implementing a loader is separate from the logic for defining the behavior of module execution itself.
I'd much rather have a static syntactically-defined way to access that information over a magically-populated not-quite-global variable like `
Introspect.context.sent. In a perfect world absolutely
module` could have been a keyword, but at this point making that change seems like an absolute no-go because it could easily break existing code.
Like look. function.sent?? Really? And it’s extremely highly context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.”
It's the exact same context-specific behavior as yield
and they are both tied to generator functions. How is that in any way unexpected?
On Sat, Aug 5, 2017 at 12:43 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
I just realised that there is also the argument that “global object cannot get current context” and other limitations applied to whether a theoretical “System/Module/Loader/
Introspect” would be a global module, or object, or keyword, or any (potentially context-sensitive) combination of these.
However, this all basically depends on what you specify in the standard, doesn’t it? :)
-
Dynamic import has a “forbidden extensions” section[1] and how it should work when it’s invoked as a CallExpression [2]
-
import.meta has a full section describing how the runtime should behave when encountering this particular property[3]
-
new global objects like Reflect, Proxy, Symbol have specifications on what they are and hoe they should be treated [4]
A theoretical global object/keyword/identifier/
special form X could be specified as <object/keyword/identifier/
avocado>. X.someProperty: when encountered, let context be Ctx, let A be B, and C be B, populate with properties from here and there and everywhere.
Or look at the AwaitExpression[5]. There are specific limits in place to guard where and how it’s used and when it is to be evaluated as AwaitExpression.
So there’s really nothing stopping you from designing a proper System/Module/Loader/
Introspect/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
Like look. function.sent?? Really? And it’s extremely highly context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.” [6]
Look. Here’s a proposal: Introspect.context.sent can appear anywhere a YieldExpress would be legal. Referencing Introspect.context.sent outside of a GeneratorBody is a Syntax Error.
And then you can use Introspect.context.target
instead of new.target
. Introspect.context.module
instead of import.meta
. Clean. Reasonable. Extensible. Future-proof.
[1] tc39.github.io/proposal-dynamic-import/#sec-forbidden-extensions
[2] tc39.github.io/proposal-dynamic-import/#sec-import-calls
[3] tc39.github.io/proposal-import-meta/#sec-source-text-module-records
[4] tc39.github.io/ecma262/#sec-reflection , tc39.github.io/ecma262/#sec-proxy-objects , tc39.github.io/ecma262/#sec-symbol-objects
[5] tc39.github.io/ecma262/#prod-AwaitExpression
[6] allenwb/ESideas/blob/master/Generator metaproperty.md
On Sat, 05 Aug 2017 at 20:40 mailto:dmitrii at dmitriid.com < mailto:dmitrii at dmitriid.com
wrote:
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
See my previous post about the multitude of keyword and keyword-like types that the language has. It’s so bad that even “metaproperty” concept itself isn’t defined in the standard except as a hardcoded new.target
[1]
If you read, for example, through the import.meta draft, you see things ripe for inclusion in a proper API object.
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language: new
is an operator[2], it gets new.target
. A function is a callable object [3], and it gets a function.sent
. Import is … I don’t know what import is [4]. It gets transformed into a separate, made-just-for-import CallExpression[5] and then it gets an import.meta
on top of that [6] (as a hardcoded “metaproperty").
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
All this is chaos from the perspective of developer experience. And truly looks like random ad-hoc solutions to immediate problems with no long-term goals. Imagine how much better and future-proof it would be if all this was in the form of a unified centralised API? There is a reason people laugh at PHP for its API and language design.
[1] www.ecma-international.org/ecma-262/#sec-meta-properties
[2] www.ecma-international.org/ecma-262/7.0/#sec-new-operator
[3] www.ecma-international.org/ecma-262/7.0/#sec-terms-and-definitions-function
[4] www.ecma-international.org/ecma-262/7.0/#sec-imports
[5] tc39.github.io/proposal-dynamic-import/#sec-left-hand-side-expressions
[6] tc39.github.io/proposal-import-meta/#sec-left-hand-side-expressions
[7] www.ecma-international.org/ecma-262/7.0/#sec-left-hand-side-expressions
On Sat, 05 Aug 2017 at 18:59 Matthew Robb
< mailto:Matthew+Robb+%3Cmatthewwrobb at gmail.com%3E
wrote:
I really can't find a good resource on direct vs indirect evaluation but my understanding is it's one of the main considerations for using a keyword over an identifier for contextual information. One example which is already in the language would be 'eval' which you can read a little about here: 2ality.com/2014/01/eval.html
Now you might be able to have an API that gets you the same result as the context sensitive keywords but it would be less ergonomic among other things: Reflect.getModuleMetaProperty(
someModuleNs, 'propName') but this becomes much more difficult to do FROM WITHIN THE MODULE ITSELF. Anything that is, let's call it tangible, cannot receive implicit contextual information it must have something passed to it that it would use to look up said information.
Sure there could be arguments made about introducing new environment type records to the top level module scope of all modules but this is potentially much more error prone and likely to lead to more and bigger questions down the road. 'module' in particular is a really bad choice imo as node/commonjs have already introduced a 'module' identifier into all of their module scopes hence module.exports = ...
. There may be solutions to working around that in one form or another BUT the 'trend' in TC39 to use keyword meta properties for context sensitive information is to avoid solving ever edge case of conflict that would impact existing code and users. It really is a fairly ripe space for powerful and ergonomic features like super
which feel like "magic". The same is true for import.meta but it may be harder to identify right off as the uses haven't all been fully introduced such as environment specific properties and potentialy other loader hooks.
NOW as I was writing this it came to mind that we DO have a new syntactic form for private data coming in the form of private fields which use a hash prefix. It would be interesting to explore using the same syntax for module scoped private fields:
console.log(#dirname);
- Matthew Robb
On Sat, Aug 5, 2017 at 12:35 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Too bad emails don’t have "thumbs up" and “+1”s :) So here’s my "+1” to you
On Sat, 05 Aug 2017 at 18:28 "T.J. Crowder"
<
">"T.J. Crowder"
wrote:
On Sat, Aug 5, 2017 at 5:05 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
So, in my opinion, the argument for not adding new global entities
such as System, or Module, or Loader (or heck, even all three of
them) being “these are not keywords, we can’t introduce them” is
really really weak.
Is anyone making that argument? I certainly am not. Not only is it possible to add more global entities, as you point out, it's been done repeatedly: Symbol
, Reflect
, etc. They just can't be keywords without breaking things. They have to be identifiers. Which means they have bindings with values. Which means those values can be copied. Which has implications.
On Sat, Aug 5, 2017 at 5:08 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a
context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-
sensitive keyword (as are a bunch of others, like super.
Is super a keyword BTW?)
My point was that this would be the only case I know of where it would be a keyword in one context but an identifier in another in the exact same production. super
, import
, etc., are always keywords. You just can't use them except in certain contexts. So I shouldn't have said "context-sensitive keyword" so much as "keyword or identifier depending on context." (But then...I did, earlier; I figured the shorthand was okay after spelling it out longhand. :-) )
But again: Maybe that's feasible. Or maybe it's not a problem passing the value around, in which case a predefined module
identifier only in module code isn't a problem anyway.
-- T.J. Crowder
There’s no code anywhere which doesn’t have an object called Symbol
invoked with new Symbol()
?
Any such code would either have a local Symbol
, would have defined a
global Symbol
that shadowed any pre-defined one (including the new global
in ES6), or would have thrown a ReferenceError or TypeError. Adding
Symbol
won't break any of that code (because the code that threw was
already broken).
The thing is, I clearly provide arguments in favour of my opinion.
We now have:
- keywords that are just keywords, really (typeof, case, break, etc.) ...
Totally fair. I understand that's where your coming from.
How can adding “properties” to semantically fundamentally different
things elegant? Or increasing the number of extremely context-dependent things? Or overriding existing keywords with new and exceedingly confusing behaviours?
This is an argument for literally never changing the language. New things always seem confusing at first, then you learn and move on.
Riiiight. Because import.meta is not a magically-populated
not-quite-global variable. Oh. Wait. That’s exactly what it is.
Introspect
is a variable with well-defined existing behavior as a
variable. import
is not a variable, it has no meaning when used as a
value until the spec assigns it a behavior. That is the point I'm trying to
get across. Is it less than ideal? Yeah, having module
as a keyword would
be perfect, but we have to work within the bounds of the language that we
have.
Even though it’s worse. import is a not-really-a-function-not-
really-an-object-not-really-a-static-not-really-a-dynamic-keyword which is clearly far worse than a properly defined API.
What is a properly defined API for this case? Access to .meta
needs to be
able to know what module you are inside of. It needs some kind of context
that ties it to the file that is asking for metadata. Within the existing
behavior of the language, we can't just make a new global object like
Symbol
or Reflect
because it has no way to know what file is asking for
metadata.
The options are either:
- Add new syntax to expose the per-module-specific information, like
import.meta
, which cannot conflict with any existing code. - Co-opt existing property-access behavior to make it behave differently
from how it would in other contexts, like
Introspect.context
. It would either have to introduce an entirely new keyword, which as I mentioned is a breaking change, or it would have to fundamentally override the behavior of accessing a property on an object to allow the object to know what module the property was accessed from.
Since new keywords are out the window, I guess it could be said that the argument here are two sides, one arguing for more syntax to keep the execution model simple (import.meta) vs keeping the syntax simple at the expense of making the runtime behavior of the code more complex by changing how property access works.
I fall on the side of syntax because changing the behavior of property access to expose the current file seems massively more complex and seems much more likely to me to confuse people.
Honestly, I’ve seen the “you learn new thing and move on” so many times, I’m getting tired of it.
I have ~10 years of experience with PHP and ~5 years of experience with Coldfusion. Both experiences are pre-2010 (both languages somewhat improved since then, even though I don’t know whether Coldfusion is still being developed).
Both are, or were, horrible languages. Both were, no doubt, driven by “you learn new things and move on”. Probably the reason why Coldfusion in its script form didn’t have “less than” and “greater than” expressed as “<“ and “>”. You had to write “gt”, “gte”, “lt”, “lte”. Loads of fun. if (x gt y)
etc.
Another argument I really don’t get is “oh, you can’t have an object, it will have to change access rules” or “import.meta isn’t a variable, it has access to local context, you can’t have that with global objects” or some such.
It’s just grammar isn’t it? Somehow nothing stops you from explicitly defining a thing called ImportCall
to randomly turn a static keyword into a function-like import(). Nothing stops you from explicitly defining behaviour for import.meta
. Nothing stops you from explicitly defining behaviour of Reflect/Proxy/Symbol/what-have-you.
However, when the question comes to, say, replacing import.meta
with X.y.z
, it becomes nigh impossible. “Changing property access rules” and all that. Somehow changing how keywords are treated is of no concern though. Somehow introducing and explicitly defining behaviour for new global objects is no biggie. But don’t you dare touch import()
, import.meta
, function.sent
. These are holy cows and their behaviour cannot be implemented in any other way than in “this new thing that you learn and move on”.
Here’s import.meta
semantics rewritten for X.y.z.
Introduction
X.y.z
Object | undefined
An object exposed through the X.y.z property. Concrete subclasses of the Abstract Module Record specification type are expected to fill this field when creating the Module Record, usually by delegating to the host environment. It is undefined by default, but must become an Object before it is ever accessed by ECMAScript code.
X.y.z available only in module context. Usage outside raises a SyntaxError.
(somehere in Left-Hand-Side Expressions I guess, too late for me to sift through the standard)
XYZ :
X.y.z
Module semantics
1.1.2 Source Text Module Records
1.1.2.1 ModuleEvaluation( ) Concrete Method
The ModuleEvaluation concrete method of a Source Text Module Record performs the following steps:
-
Let module be this Source Text Module Record.
-
Assert: ModuleDeclarationInstantiation has already been invoked on module and successfully completed.
-
Assert: module.[[Realm]] is not undefined.
-
If module.[[Evaluated]] is true, return undefined.
-
Set module.[[Evaluated]] to true.
-
For each String required that is an element of module.[[RequestedModules]], do
-
Let requiredModule be ! HostResolveImportedModule(module, required).
-
NOTE: ModuleDeclarationInstantiation must be completed prior to invoking this method, so every requested module is guaranteed to resolve successfully.
-
Perform ? requiredModule.ModuleEvaluation().
-
Let moduleCxt be a new ECMAScript code execution context.
-
Set the Function of moduleCxt to null.
-
Set the Realm of moduleCxt to module.[[Realm]].
-
Set the ScriptOrModule of moduleCxt to module.
-
Assert: module has been linked and declarations in its module environment have been instantiated.
-
Set the VariableEnvironment of moduleCxt to module.[[Environment]].
-
Set the LexicalEnvironment of moduleCxt to module.[[Environment]].
-
Suspend the currently running execution context.
-
Push moduleCxt on to the execution context stack; moduleCxt is now the running execution context.
-
Let importMeta be ObjectCreate(null).
-
Let importMetaValues be ! HostGetImportMetaProperties(module).
-
For each Record {[[Key]], [[Value]]} p that is an element of importMetaValues,
-
Perform ! CreateDataProperty(importMeta, p.[[Key]], p.[[Value]]).
-
Perform ! HostFinalizeImportMeta(importMeta, module).
-
Set module.[[ImportMeta]] to importMeta.
-
Let result be the result of evaluating module.[[ECMAScriptCode]].
-
Suspend moduleCxt and remove it from the execution context stack.
-
Resume the context that is now on the top of the execution context stack as the running execution context.
-
Return Completion(result).
etc.
On Sat, 05 Aug 2017 at 23:50 Logan Smyth
< mailto:Logan Smyth <loganfsmyth at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
The thing is, I clearly provide arguments in favour of my opinion.
We now have:
- keywords that are just keywords, really (typeof, case, break, etc.)
...
Totally fair. I understand that's where your coming from.
How can adding “properties” to semantically fundamentally different things elegant? Or increasing the number of extremely context-dependent things? Or overriding existing keywords with new and exceedingly confusing behaviours?
This is an argument for literally never changing the language. New things always seem confusing at first, then you learn and move on.
Riiiight. Because import.meta is not a magically-populated not-quite-global variable. Oh. Wait. That’s exactly what it is.
Introspect
is a variable with well-defined existing behavior as a variable. import
is not a variable, it has no meaning when used as a value until the spec assigns it a behavior. That is the point I'm trying to get across. Is it less than ideal? Yeah, having module
as a keyword would be perfect, but we have to work within the bounds of the language that we have.
Even though it’s worse. import is a not-really-a-function-not-
really-an-object-not-really-a-
static-not-really-a-dynamic-
keyword which is clearly far worse than a properly defined API.
What is a properly defined API for this case? Access to .meta
needs to be able to know what module you are inside of. It needs some kind of context that ties it to the file that is asking for metadata. Within the existing behavior of the language, w
e can't just make a new global object like Symbol
or Reflect
because it has no way to know what file is asking for metadata.
The options are either:
-
Add new syntax to expose the per-module-specific information, like
import.meta
, which cannot conflict with any existing code. -
Co-opt existing property-access behavior to make it behave differently from how it would in other contexts, like
Introspect.context
. It would either have to introduce an entirely new keyword, which as I mentioned is a breaking change, or it would have to fundamentally override the behavior of accessing a property on an object to allow the object to know what module the property was accessed from.
Since new keywords are out the window, I guess it could be said that the argument here are two sides, one arguing for more syntax to keep the execution model simple (import.meta) vs keeping the syntax simple at the expense of making the runtime behavior of the code more complex by changing how property access works.
I fall on the side of syntax because changing the behavior of property access to expose the current file seems massively more complex and seems much more likely to me to confuse people.
On Sat, Aug 5, 2017 at 2:29 PM, Jordan Harband
< mailto:ljharb at gmail.com
wrote:
There’s no code anywhere which doesn’t have an object called Symbol invoked with new Symbol()
?
Any such code would either have a local Symbol
, would have defined a global Symbol
that shadowed any pre-defined one (including the new global in ES6), or would have thrown a ReferenceError or TypeError. Adding Symbol
won't break any of that code (because the code that threw was already broken).
On Sat, Aug 5, 2017 at 2:20 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
[Reflect] Not a breaking change because adding a new global isn't new syntax, it's just a new variable that exists and essentially can't break much.
It either isn’t a breaking change, or can’t break much. It cannot be both.
Not a breaking change because this constructor did not exist before.
Are you entirely sure? There’s no code anywhere which doesn’t have an object called Symbol invoked with new Symbol()
? The moment you add a global anything, you break someone’s code somewhere.
I don't think I'd call super properties metaproperties for this reason.
super
property access is its own class-related syntax that isn't related. I do think it sets a perfectly reasonable guideline that makes it clear most people have no problem accessing properties off of keywords.
I fail to see where you see guidelines specced out in the specifications of metaproperties or superproperties. “Most people” end up just stuck with these because they are either unaware that these changes are coming or are not active enough to voice their concerns.
The biggest issue for me in this whole thread is that it's all extremely opinion-based. What one person calls complex another would call elegant.
The thing is, I clearly provide arguments in favour of my opinion. Let me just summarise them for you.
We now have:
-
keywords that are just keywords, really (typeof, case, break, etc.)
-
keywords that are just keywords, but don’t even exist in a language. They are reserved for future use in various contexts: always reserved, only in strict mode, only in module code etc. (enum, public, private, await etc.). May never be used and may possibly be removed, as some keywords have been (int, byte, char etc.)
-
literals that are basically keywords (null, true, false)
-
non-keywords that are for all intents and purposes keywords (eval, arguments)
-
keywords that look like objects (because they have additional properties) which are not objects (new with new.target)
-
keywords that look like functions (because they are invoked like functions and return values like functions) which are not functions (import and import())
-
keywords that look like objects and functions but are neither (import and import() and import.meta)
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language:
-
new
is an operator[2], it getsnew.target
-
A function is a callable object [3], and it gets a
function.sent
-
import is … I don’t know what import is. It gets transformed into a separate, made-just-for-import CallExpression and then it gets an
import.meta
on top of that (as a hardcoded “metaproperty”).
All of the above are basic facts about the language as it exists now. How can adding “properties” to semantically fundamentally different things elegant? Or increasing the number of extremely context-dependent things? Or overriding existing keywords with new and exceedingly confusing behaviours?
So there’s really nothing stopping you from designing a proper System/Module/Loader/Introspec
t/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
I don't think anyone has claimed that
import.meta
is meant as a replacement for these. We still need a loader spec
and a System spec and an Introspect spec, and ... That is why all these random additions to vastly different things in the language look like ad-hoc shortsighted solutions with no respect for the language or its evolution. “We need a thing, we have nowhere to put this thing, let’s add this thing to a keyword, because new.target (bad design) has opened a door for us”.
I'd much rather have a static syntactically-defined way to access that information over a magically-populated not-quite-global variable like
Introspect.context.sent
.
Riiiight. Because import.meta is not a magically-populated not-quite-global variable. Oh. Wait. That’s exactly what it is.
Even though it’s worse. import is a not-really-a-function-not-real
ly-an-object-not-really-a-stat
ic-not-really-a-dynamic-keywor
d which is clearly far worse than a properly defined API.
On Sat, 05 Aug 2017 at 22:37 Logan Smyth
< mailto:Logan+Smyth+%3Cloganfsmyth at gmail.com%3E
wrote:
await
could be added because there is no case in existing code where await <expression>
would have been valid code, so it is backward-compatible. The same applies for all of the meta-property proposals. The same is not true for Introspect.context.module
. It's not just a question of it a given construct could be made to behave the way you want, it's also a question of it it would be compatible with existing code.
Introduced in ECMASCRIPT 2015: Reflect is a built-in object that provides methods for interceptable JavaScript operations.
Not a breaking change because adding a new global isn't new syntax, it's just a new variable that exists and essentially can't break much.
Introduced in ECMASCRIPT 2015: The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax "new Symbol()”.
Not a breaking change because this constructor did not exist before.
ECMAScript 5 introduced: yield, let
Not a breaking change because those two can only occur in locations where they can't conflict with existing ES5 code. The extremely limited cases where let
could be in conflict are explicitly handled in the grammar to prevent breaking changes.
ECMASCRIPT 6 introduced: await as a reserved word in module code
Same as above.
So, introducting new things into the language is not really such a big problem as it’s made out to be ;)
No-one said introducing things was not possible, but we can't break existing code.
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
I don't think I'd call super properties metaproperties for this reason. super
property access is its own class-related syntax that isn't related.
I do think it sets a perfectly reasonable guideline that makes it clear most people have no problem accessing properties off of keywords.
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
This seems to be the core of your argument, but I honestly don't quite see why it is more complex/chaotic. The biggest issue for me in this whole thread is that it's all extremely opinion-based. What one person calls complex another would call elegant.
What specifically is difficult to reason about that wasn't already complex? Same for function.arguments
. You're saying you think it's better to have an automatically-created arguments
variable in every single function instead of having syntax to access it? arguments
and this
as two auto-initialized bindings are some of the most confusing parts of JS.
So there’s really nothing stopping you from designing a proper System/Module/Loader/
Introspec
t/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
I don't think anyone has claimed that import.meta
is meant as a replacement for these. We still need a loader spec, but having a syntactic way to access data about the current active module is absolutely a useful thing to have. It's no different than CommonJS's __dirname and __filename among others. The logic for implementing a loader is separate from the logic for defining the behavior of module execution itself.
I'd much rather have a static syntactically-defined way to access that information over a magically-populated not-quite-global variable like `
Introspect.context.sent. In a perfect world absolutely
module` could have been a keyword, but at this point making that change seems like an absolute no-go because it could easily break existing code.
Like look. function.sent?? Really? And it’s extremely highly context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.”
It's the exact same context-specific behavior as yield
and they are both tied to generator functions. How is that in any way unexpected?
On Sat, Aug 5, 2017 at 12:43 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
I just realised that there is also the argument that “global object cannot get current context” and other limitations applied to whether a theoretical “System/Module/Loader/Introspe
ct” would be a global module, or object, or keyword, or any (potentially context-sensitive) combination of these.
However, this all basically depends on what you specify in the standard, doesn’t it? :)
-
Dynamic import has a “forbidden extensions” section[1] and how it should work when it’s invoked as a CallExpression [2]
-
import.meta has a full section describing how the runtime should behave when encountering this particular property[3]
-
new global objects like Reflect, Proxy, Symbol have specifications on what they are and hoe they should be treated [4]
A theoretical global object/keyword/identifier/spec
ial form X could be specified as <object/keyword/identifier/avo
cado>. X.someProperty: when encountered, let context be Ctx, let A be B, and C be B, populate with properties from here and there and everywhere.
Or look at the AwaitExpression[5]. There are specific limits in place to guard where and how it’s used and when it is to be evaluated as AwaitExpression.
So there’s really nothing stopping you from designing a proper System/Module/Loader/Introspec
t/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
Like look. function.sent?? Really? And it’s extremely highly context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.” [6]
Look. Here’s a proposal: Introspect.context.sent can appear anywhere a YieldExpress would be legal. Referencing Introspect.context.sent outside of a GeneratorBody is a Syntax Error.
And then you can use Introspect.context.target
instead of new.target
. Introspect.context.module
instead of import.meta
. Clean. Reasonable. Extensible. Future-proof.
[1] tc39.github.io/proposal-dynamic-import/#sec-forbidden-extensions
[2] tc39.github.io/proposal-dynamic-import/#sec-import-calls
[3] tc39.github.io/proposal-import-meta/#sec-source-text-module-records
[4] tc39.github.io/ecma262/#sec-reflection , tc39.github.io/ecma262/#sec-proxy-objects , tc39.github.io/ecma262/#sec-symbol-objects
[5] tc39.github.io/ecma262/#prod-AwaitExpression
[6] allenwb/ESideas/blob/master/Generator metaproperty.md
On Sat, 05 Aug 2017 at 20:40 mailto:dmitrii at dmitriid.com < mailto:dmitrii at dmitriid.com
wrote:
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
See my previous post about the multitude of keyword and keyword-like types that the language has. It’s so bad that even “metaproperty” concept itself isn’t defined in the standard except as a hardcoded new.target
[1]
If you read, for example, through the import.meta draft, you see things ripe for inclusion in a proper API object.
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language: new
is an operator[2], it gets new.target
. A function is a callable object [3], and it gets a function.sent
. Import is … I don’t know what import is [4]. It gets transformed into a separate, made-just-for-import CallExpression[5] and then it gets an import.meta
on top of that [6] (as a hardcoded “metaproperty").
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
All this is chaos from the perspective of developer experience. And truly looks like random ad-hoc solutions to immediate problems with no long-term goals. Imagine how much better and future-proof it would be if all this was in the form of a unified centralised API? There is a reason people laugh at PHP for its API and language design.
[1] www.ecma-international.org/ecma-262/#sec-meta-properties
[2] www.ecma-international.org/ecma-262/7.0/#sec-new-operator
[3] www.ecma-international.org/ecma-262/7.0/#sec-terms-and-definitions-function
[4] www.ecma-international.org/ecma-262/7.0/#sec-imports
[5] tc39.github.io/proposal-dynamic-import/#sec-left-hand-side-expressions
[6] tc39.github.io/proposal-import-meta/#sec-left-hand-side-expressions
[7] www.ecma-international.org/ecma-262/7.0/#sec-left-hand-side-expressions
On Sat, 05 Aug 2017 at 18:59 Matthew Robb
< mailto:Matthew+Robb+%3Cmatthewwrobb at gmail.com%3E
wrote:
I really can't find a good resource on direct vs indirect evaluation but my understanding is it's one of the main considerations for using a keyword over an identifier for contextual information. One example which is already in the language would be 'eval' which you can read a little about here: 2ality.com/2014/01/eval.html
Now you might be able to have an API that gets you the same result as the context sensitive keywords but it would be less ergonomic among other things: Reflect.getModuleMetaProperty(
someModuleNs, 'propName') but this becomes much more difficult to do FROM WITHIN THE MODULE ITSELF. Anything that is, let's call it tangible, cannot receive implicit contextual information it must have something passed to it that it would use to look up said information.
Sure there could be arguments made about introducing new environment type records to the top level module scope of all modules but this is potentially much more error prone and likely to lead to more and bigger questions down the road. 'module' in particular is a really bad choice imo as node/commonjs have already introduced a 'module' identifier into all of their module scopes hence module.exports = ...
. There may be solutions to working around that in one form or another BUT the 'trend' in TC39 to use keyword meta properties for context sensitive information is to avoid solving ever edge case of conflict that would impact existing code and users. It really is a fairly ripe space for powerful and ergonomic features like super
which feel like "magic". The same is true for import.meta but it may be harder to identify right off as the uses haven't all been fully introduced such as environment specific properties and potentialy other loader hooks.
NOW as I was writing this it came to mind that we DO have a new syntactic form for private data coming in the form of private fields which use a hash prefix. It would be interesting to explore using the same syntax for module scoped private fields:
console.log(#dirname);
- Matthew Robb
On Sat, Aug 5, 2017 at 12:35 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Too bad emails don’t have "thumbs up" and “+1”s :) So here’s my "+1” to you
On Sat, 05 Aug 2017 at 18:28 "T.J. Crowder"
<
">"T.J. Crowder"
wrote:
On Sat, Aug 5, 2017 at 5:05 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
So, in my opinion, the argument for not adding new global entities
such as System, or Module, or Loader (or heck, even all three of
them) being “these are not keywords, we can’t introduce them” is
really really weak.
Is anyone making that argument? I certainly am not. Not only is it possible to add more global entities, as you point out, it's been done repeatedly: Symbol
, Reflect
, etc. They just can't be keywords without breaking things. They have to be identifiers. Which means they have bindings with values. Which means those values can be copied. Which has implications.
On Sat, Aug 5, 2017 at 5:08 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a
context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-
sensitive keyword (as are a bunch of others, like super.
Is super a keyword BTW?)
My point was that this would be the only case I know of where it would be a keyword in one context but an identifier in another in the exact same production. super
, import
, etc., are always keywords. You just can't use them except in certain contexts. So I shouldn't have said "context-sensitive keyword" so much as "keyword or identifier depending on context." (But then...I did, earlier; I figured the shorthand was okay after spelling it out longhand. :-) )
But again: Maybe that's feasible. Or maybe it's not a problem passing the value around, in which case a predefined module
identifier only in module code isn't a problem anyway.
-- T.J. Crowder
Nothing stops you from explicitly defining behaviour for
import.meta
.
Nothing stops you from explicitly defining behaviour of Reflect/Proxy/Symbol/what-have-you.
However, when the question comes to, say, replacing
import.meta
withX.y.z
, it becomes nigh impossible.
As has been mentioned many times, backward-compatibility of a critical feature of JS. We can't take over the behavior of a random variable have have it do something else because it can break existing code. Other features are done carefully to avoid this issue, but your proposal does not avoid that.
Here’s
import.meta
semantics rewritten for X.y.z.
X.y.z available only in module context. Usage outside raises a
SyntaxError.
That will break existing code, making it immediately a no-go.
Dimitri I think what you're missing is that while yes the current proposal and your proposal are not fundamentally different, the difference is that overloading keywords is overloading into previously non existent design space whereas overloading globals MUST consider the ramifications of overloading OVER existing design space.
On Aug 5, 2017 6:21 PM, "Logan Smyth" <loganfsmyth at gmail.com> wrote:
Nothing stops you from explicitly defining behaviour for
import.meta
.
Nothing stops you from explicitly defining behaviour of Reflect/Proxy/Symbol/what-have-you.
However, when the question comes to, say, replacing
import.meta
withX.y.z
, it becomes nigh impossible.
As has been mentioned many times, backward-compatibility of a critical feature of JS. We can't take over the behavior of a random variable have have it do something else because it can break existing code. Other features are done carefully to avoid this issue, but your proposal does not avoid that.
Here’s
import.meta
semantics rewritten for X.y.z.
X.y.z available only in module context. Usage outside raises a
SyntaxError.
That will break existing code, making it immediately a no-go.
I understand that perfectly. What you are missing though is that extending keywords every which way leads to chaos in the language and is, ultimately, a very shortsighted solution. I will not reiterate the long list of how this affects the language.
For posterity, tc39/ecma262#968
You may argue all you want about how elegant these solutions are, or how useful they are, or how they are needed, but this doesn’t change the fact that they look and feel chaotic, ad-hoc, half-baked solutions.
Moreover. All the talk about how horrible it would be to break existing code, existing design space etc. etc. clearly makes introduction of any complex, well designed APIs into the language an impossibility.
Oh, well. I’ve fought my fight. Onwards with “elegant solution of slapping metaproperties and function-like behaviour on top of existing things”. Hopefully, WebAssembly gets its GC and DOM support before this house of cards falls down.
On Sun, 06 Aug 2017 at 01:16 Matthew Robb
< mailto:Matthew Robb <matthewwrobb at gmail.com>
wrote:
a, pre, code, a:link, body { word-wrap: break-word !important; }
Dimitri I think what you're missing is that while yes the current proposal and your proposal are not fundamentally different, the difference is that overloading keywords is overloading into previously non existent design space whereas overloading globals MUST consider the ramifications of overloading OVER existing design space.
On Aug 5, 2017 6:21 PM, "Logan Smyth" < mailto:loganfsmyth at gmail.com
wrote:
Nothing stops you from explicitly defining behaviour for import.meta
. Nothing stops you from explicitly defining behaviour of Reflect/Proxy/Symbol/what-
have
-you.
However, when the question comes to, say, replacing import.meta
with X.y.z
, it becomes nigh impossible.
As has been mentioned many times, backward-compatibility of a critical feature of JS. We can't take over the behavior of a random variable have have it do something else because it can break existing code. Other features are done carefully to avoid this issue, but your proposal does not avoid that.
Here’s import.meta
semantics rewritten for X.y.z.
X.y.z available only in module context. Usage outside raises a SyntaxError.
That will break existing code, making it immediately a no-go.
On Sat, Aug 5, 2017 at 3:09 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Honestly, I’ve seen the “you learn new thing and move on” so many times, I’m getting tired of it.
I have ~10 years of experience with PHP and ~5 years of experience with Coldfusion. Both experiences are pre-2010 (both languages somewhat improved since then, even though I don’t know whether Coldfusion is still being developed).
Both are, or were, horrible languages. Both were, no doubt, driven by “you learn new things and move on”. Probably the reason why Coldfusion in its script form didn’t have “less than” and “greater than” expressed as “<“ and “>”. You had to write “gt”, “gte”, “lt”, “lte”. Loads of fun. if (x gt y)
etc.
Another argument I really don’t get is “oh, you can’t have an object, it will have to change access rules” or “import.meta isn’t a variable, it has access to local context, you can’t have that with global objects” or some such.
It’s just grammar isn’t it? Somehow nothing stops you from explicitly defining a thing called ImportCall
to randomly turn a static keyword into a function-like import(). Nothing stops you from explicitly defining behaviour for import.meta
. Nothing stops you from explicitly defining behaviour of Reflect/Proxy/Symbol/what-have
-you.
However, when the question comes to, say, replacing import.meta
with X.y.z
, it becomes nigh impossible. “Changing property access rules” and all that. Somehow changing how keywords are treated is of no concern though. Somehow introducing and explicitly defining behaviour for new global objects is no biggie. But don’t you dare touch import()
, import.meta
, function.sent
. These are holy cows and their behaviour cannot be implemented in any other way than in “this new thing that you learn and move on”.
Here’s import.meta
semantics rewritten for X.y.z.
Introduction
X.y.z
Object | undefined
An object exposed through the X.y.z property. Concrete subclasses of the Abstract Module Record specification type are expected to fill this field when creating the Module Record, usually by delegating to the host environment. It is undefined by default, but must become an Object before it is ever accessed by ECMAScript code.
X.y.z available only in module context. Usage outside raises a SyntaxError.
(somehere in Left-Hand-Side Expressions I guess, too late for me to sift through the standard)
XYZ :
X.y.z
Module semantics
1.1.2 Source Text Module Records
1.1.2.1 ModuleEvaluation( ) Concrete Method
The ModuleEvaluation concrete method of a Source Text Module Record performs the following steps:
-
Let module be this Source Text Module Record.
-
Assert: ModuleDeclarationInstantiation has already been invoked on module and successfully completed.
-
Assert: module.[[Realm]] is not undefined.
-
If module.[[Evaluated]] is true, return undefined.
-
Set module.[[Evaluated]] to true.
-
For each String required that is an element of module.[[RequestedModules]], do
-
Let requiredModule be ! HostResolveImportedModule(modu
le, required).
-
NOTE: ModuleDeclarationInstantiation must be completed prior to invoking this method, so every requested module is guaranteed to resolve successfully.
-
Perform ? requiredModule.ModuleEvaluatio
n().
-
Let moduleCxt be a new ECMAScript code execution context.
-
Set the Function of moduleCxt to null.
-
Set the Realm of moduleCxt to module.[[Realm]].
-
Set the ScriptOrModule of moduleCxt to module.
-
Assert: module has been linked and declarations in its module environment have been instantiated.
-
Set the VariableEnvironment of moduleCxt to module.[[Environment]].
-
Set the LexicalEnvironment of moduleCxt to module.[[Environment]].
-
Suspend the currently running execution context.
-
Push moduleCxt on to the execution context stack; moduleCxt is now the running execution context.
-
Let importMeta be ObjectCreate(null).
-
Let importMetaValues be ! HostGetImportMetaProperties(mo
dule).
-
For each Record {[[Key]], [[Value]]} p that is an element of importMetaValues,
-
Perform ! CreateDataProperty(importMeta, p.[[Key]], p.[[Value]]).
-
Perform ! HostFinalizeImportMeta(importM
eta, module).
-
Set module.[[ImportMeta]] to importMeta.
-
Let result be the result of evaluating module.[[ECMAScriptCode]].
-
Suspend moduleCxt and remove it from the execution context stack.
-
Resume the context that is now on the top of the execution context stack as the running execution context.
-
Return Completion(result).
etc.
On Sat, 05 Aug 2017 at 23:50 Logan Smyth
< mailto:Logan+Smyth+%3Cloganfsmyth at gmail.com%3E
wrote:
The thing is, I clearly provide arguments in favour of my opinion.
We now have:
- keywords that are just keywords, really (typeof, case, break, etc.)
...
Totally fair. I understand that's where your coming from.
How can adding “properties” to semantically fundamentally different things elegant? Or increasing the number of extremely context-dependent things? Or overriding existing keywords with new and exceedingly confusing behaviours?
This is an argument for literally never changing the language. New things always seem confusing at first, then you learn and move on.
Riiiight. Because import.meta is not a magically-populated not-quite-global variable. Oh. Wait. That’s exactly what it is.
Introspect
is a variable with well-defined existing behavior as a variable. import
is not a variable, it has no meaning when used as a value until the spec assigns it a behavior. That is the point I'm trying to get across. Is it less than ideal? Yeah, having module
as a keyword would be perfect, but we have to work within the bounds of the language that we have.
Even though it’s worse. import is a not-really-a-function-not-
real
ly-an-object-not-really-a-
stat
ic-not-really-a-dynamic-
keywor
d which is clearly far worse than a properly defined API.
What is a properly defined API for this case? Access to .meta
needs to be able to know what module you are inside of. It needs some kind of context that ties it to the file that is asking for metadata. Within the existing behavior of the language, w
e can't just make a new global object like Symbol
or Reflect
because it has no way to know what file is asking for metadata.
The options are either:
-
Add new syntax to expose the per-module-specific information, like
import.meta
, which cannot conflict with any existing code. -
Co-opt existing property-access behavior to make it behave differently from how it would in other contexts, like
Introspect.context
. It would either have to introduce an entirely new keyword, which as I mentioned is a breaking change, or it would have to fundamentally override the behavior of accessing a property on an object to allow the object to know what module the property was accessed from.
Since new keywords are out the window, I guess it could be said that the argument here are two sides, one arguing for more syntax to keep the execution model simple (import.meta) vs keeping the syntax simple at the expense of making the runtime behavior of the code more complex by changing how property access works.
I fall on the side of syntax because changing the behavior of property access to expose the current file seems massively more complex and seems much more likely to me to confuse people.
On Sat, Aug 5, 2017 at 2:29 PM, Jordan Harband
< mailto:ljharb at gmail.com
wrote:
There’s no code anywhere which doesn’t have an object called Symbol invoked with new Symbol()
?
Any such code would either have a local Symbol
, would have defined a global Symbol
that shadowed any pre-defined one (including the new global in ES6), or would have thrown a ReferenceError or TypeError. Adding Symbol
won't break any of that code (because the code that threw was already broken).
On Sat, Aug 5, 2017 at 2:20 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
[Reflect] Not a breaking change because adding a new global isn't new syntax, it's just a new variable that exists and essentially can't break much.
It either isn’t a breaking change, or can’t break much. It cannot be both.
Not a breaking change because this constructor did not exist before.
Are you entirely sure? There’s no code anywhere which doesn’t have an object called Symbol invoked with new Symbol()
? The moment you add a global anything, you break someone’s code somewhere.
I don't think I'd call super properties metaproperties for this reason.
super
property access is its own class-related syntax that isn't related. I do think it sets a perfectly reasonable guideline that makes it clear most people have no problem accessing properties off of keywords.
I fail to see where you see guidelines specced out in the specifications of metaproperties or superproperties. “Most people” end up just stuck with these because they are either unaware that these changes are coming or are not active enough to voice their concerns.
The biggest issue for me in this whole thread is that it's all extremely opinion-based. What one person calls complex another would call elegant.
The thing is, I clearly provide arguments in favour of my opinion. Let me just summarise them for you.
We now have:
-
keywords that are just keywords, really (typeof, case, break, etc.)
-
keywords that are just keywords, but don’t even exist in a language. They are reserved for future use in various contexts: always reserved, only in strict mode, only in module code etc. (enum, public, private, await etc.). May never be used and may possibly be removed, as some keywords have been (int, byte, char etc.)
-
literals that are basically keywords (null, true, false)
-
non-keywords that are for all intents and purposes keywords (eval, arguments)
-
keywords that look like objects (because they have additional properties) which are not objects (new with new.target)
-
keywords that look like functions (because they are invoked like functions and return values like functions) which are not functions (import and import())
-
keywords that look like objects and functions but are neither (import and import() and import.meta)
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language:
-
new
is an operator[2], it getsnew.target
-
A function is a callable object [3], and it gets a
function.sent
-
import is … I don’t know what import is. It gets transformed into a separate, made-just-for-import CallExpression and then it gets an
import.meta
on top of that (as a hardcoded “metaproperty”).
All of the above are basic facts about the language as it exists now. How can adding “properties” to semantically fundamentally different things elegant? Or increasing the number of extremely context-dependent things? Or overriding existing keywords with new and exceedingly confusing behaviours?
So there’s really nothing stopping you from designing a proper System/Module/Loader/Introspec
t/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
I don't think anyone has claimed that
import.meta
is meant as a replacement for these. We still need a loader spec
and a System spec and an Introspect spec, and ... That is why all these random additions to vastly different things in the language look like ad-hoc shortsighted solutions with no respect for the language or its evolution. “We need a thing, we have nowhere to put this thing, let’s add this thing to a keyword, because new.target (bad design) has opened a door for us”.
I'd much rather have a static syntactically-defined way to access that information over a magically-populated not-quite-global variable like
Introspect.context.sent
.
Riiiight. Because import.meta is not a magically-populated not-quite-global variable. Oh. Wait. That’s exactly what it is.
Even though it’s worse. import is a not-really-a-function-not-real
ly-an-object-not-really-a-stat
ic-not-really-a-dynamic-keywor
d which is clearly far worse than a properly defined API.
On Sat, 05 Aug 2017 at 22:37 Logan Smyth
< mailto:Logan+Smyth+%3Cloganfsmyth at gmail.com%3E
wrote:
await
could be added because there is no case in existing code where await <expression>
would have been valid code, so it is backward-compatible. The same applies for all of the meta-property proposals. The same is not true for Introspect.context.module
. It's not just a question of it a given construct could be made to behave the way you want, it's also a question of it it would be compatible with existing code.
Introduced in ECMASCRIPT 2015: Reflect is a built-in object that provides methods for interceptable JavaScript operations.
Not a breaking change because adding a new global isn't new syntax, it's just a new variable that exists and essentially can't break much.
Introduced in ECMASCRIPT 2015: The Symbol() function returns a value of type symbol, has static properties that expose several members of built-in objects, has static methods that expose the global symbol registry, and resembles a built-in object class but is incomplete as a constructor because it does not support the syntax "new Symbol()”.
Not a breaking change because this constructor did not exist before.
ECMAScript 5 introduced: yield, let
Not a breaking change because those two can only occur in locations where they can't conflict with existing ES5 code. The extremely limited cases where let
could be in conflict are explicitly handled in the grammar to prevent breaking changes.
ECMASCRIPT 6 introduced: await as a reserved word in module code
Same as above.
So, introducting new things into the language is not really such a big problem as it’s made out to be ;)
No-one said introducing things was not possible, but we can't break existing code.
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
I don't think I'd call super properties metaproperties for this reason. super
property access is its own class-related syntax that isn't related.
I do think it sets a perfectly reasonable guideline that makes it clear most people have no problem accessing properties off of keywords.
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
This seems to be the core of your argument, but I honestly don't quite see why it is more complex/chaotic. The biggest issue for me in this whole thread is that it's all extremely opinion-based. What one person calls complex another would call elegant.
What specifically is difficult to reason about that wasn't already complex? Same for function.arguments
. You're saying you think it's better to have an automatically-created arguments
variable in every single function instead of having syntax to access it? arguments
and this
as two auto-initialized bindings are some of the most confusing parts of JS.
So there’s really nothing stopping you from designing a proper System/Module/Loader/
Introspec
t/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
I don't think anyone has claimed that import.meta
is meant as a replacement for these. We still need a loader spec, but having a syntactic way to access data about the current active module is absolutely a useful thing to have. It's no different than CommonJS's __dirname and __filename among others. The logic for implementing a loader is separate from the logic for defining the behavior of module execution itself.
I'd much rather have a static syntactically-defined way to access that information over a magically-populated not-quite-global variable like `
Introspect.context.sent. In a perfect world absolutely
module` could have been a keyword, but at this point making that change seems like an absolute no-go because it could easily break existing code.
Like look. function.sent?? Really? And it’s extremely highly context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.”
It's the exact same context-specific behavior as yield
and they are both tied to generator functions. How is that in any way unexpected?
On Sat, Aug 5, 2017 at 12:43 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
I just realised that there is also the argument that “global object cannot get current context” and other limitations applied to whether a theoretical “System/Module/Loader/Introspe
ct” would be a global module, or object, or keyword, or any (potentially context-sensitive) combination of these.
However, this all basically depends on what you specify in the standard, doesn’t it? :)
-
Dynamic import has a “forbidden extensions” section[1] and how it should work when it’s invoked as a CallExpression [2]
-
import.meta has a full section describing how the runtime should behave when encountering this particular property[3]
-
new global objects like Reflect, Proxy, Symbol have specifications on what they are and hoe they should be treated [4]
A theoretical global object/keyword/identifier/spec
ial form X could be specified as <object/keyword/identifier/avo
cado>. X.someProperty: when encountered, let context be Ctx, let A be B, and C be B, populate with properties from here and there and everywhere.
Or look at the AwaitExpression[5]. There are specific limits in place to guard where and how it’s used and when it is to be evaluated as AwaitExpression.
So there’s really nothing stopping you from designing a proper System/Module/Loader/Introspec
t/Avocado or any subset thereof instead of slapping “metaproperties” on everything in sight :)
Like look. function.sent?? Really? And it’s extremely highly context-specific: “function.sent can appear anywhere a YieldExpress would be legal. Referencing function.sent outside of a GeneratorBody is a Syntax Error.” [6]
Look. Here’s a proposal: Introspect.context.sent can appear anywhere a YieldExpress would be legal. Referencing Introspect.context.sent outside of a GeneratorBody is a Syntax Error.
And then you can use Introspect.context.target
instead of new.target
. Introspect.context.module
instead of import.meta
. Clean. Reasonable. Extensible. Future-proof.
[1] tc39.github.io/proposal-dynamic-import/#sec-forbidden-extensions
[2] tc39.github.io/proposal-dynamic-import/#sec-import-calls
[3] tc39.github.io/proposal-import-meta/#sec-source-text-module-records
[4] tc39.github.io/ecma262/#sec-reflection , tc39.github.io/ecma262/#sec-proxy-objects , tc39.github.io/ecma262/#sec-symbol-objects
[5] tc39.github.io/ecma262/#prod-AwaitExpression
[6] allenwb/ESideas/blob/master/Generator metaproperty.md
On Sat, 05 Aug 2017 at 20:40 mailto:dmitrii at dmitriid.com < mailto:dmitrii at dmitriid.com
wrote:
The problem with “metaproperties” is that they make the language more complex, chaotic and make it difficult to reason about the language.
See my previous post about the multitude of keyword and keyword-like types that the language has. It’s so bad that even “metaproperty” concept itself isn’t defined in the standard except as a hardcoded new.target
[1]
If you read, for example, through the import.meta draft, you see things ripe for inclusion in a proper API object.
It gets even worse. Because “metaproperties” are not just attached to keywords. They are attached to keywords which have fundamentally different semantics in the language: new
is an operator[2], it gets new.target
. A function is a callable object [3], and it gets a function.sent
. Import is … I don’t know what import is [4]. It gets transformed into a separate, made-just-for-import CallExpression[5] and then it gets an import.meta
on top of that [6] (as a hardcoded “metaproperty").
Don’t forget that super
gets its own properties. Since there’s no specification of what “metaproperties” are, they are just called superproperties[7] in the grammar. Because reasons.
All this is chaos from the perspective of developer experience. And truly looks like random ad-hoc solutions to immediate problems with no long-term goals. Imagine how much better and future-proof it would be if all this was in the form of a unified centralised API? There is a reason people laugh at PHP for its API and language design.
[1] www.ecma-international.org/ecma-262/#sec-meta-properties
[2] www.ecma-international.org/ecma-262/7.0/#sec-new-operator
[3] www.ecma-international.org/ecma-262/7.0/#sec-terms-and-definitions-function
[4] www.ecma-international.org/ecma-262/7.0/#sec-imports
[5] tc39.github.io/proposal-dynamic-import/#sec-left-hand-side-expressions
[6] tc39.github.io/proposal-import-meta/#sec-left-hand-side-expressions
[7] www.ecma-international.org/ecma-262/7.0/#sec-left-hand-side-expressions
On Sat, 05 Aug 2017 at 18:59 Matthew Robb
< mailto:Matthew+Robb+%3Cmatthewwrobb at gmail.com%3E
wrote:
I really can't find a good resource on direct vs indirect evaluation but my understanding is it's one of the main considerations for using a keyword over an identifier for contextual information. One example which is already in the language would be 'eval' which you can read a little about here: 2ality.com/2014/01/eval.html
Now you might be able to have an API that gets you the same result as the context sensitive keywords but it would be less ergonomic among other things: Reflect.getModuleMetaProperty(
someModuleNs, 'propName') but this becomes much more difficult to do FROM WITHIN THE MODULE ITSELF. Anything that is, let's call it tangible, cannot receive implicit contextual information it must have something passed to it that it would use to look up said information.
Sure there could be arguments made about introducing new environment type records to the top level module scope of all modules but this is potentially much more error prone and likely to lead to more and bigger questions down the road. 'module' in particular is a really bad choice imo as node/commonjs have already introduced a 'module' identifier into all of their module scopes hence module.exports = ...
. There may be solutions to working around that in one form or another BUT the 'trend' in TC39 to use keyword meta properties for context sensitive information is to avoid solving ever edge case of conflict that would impact existing code and users. It really is a fairly ripe space for powerful and ergonomic features like super
which feel like "magic". The same is true for import.meta but it may be harder to identify right off as the uses haven't all been fully introduced such as environment specific properties and potentialy other loader hooks.
NOW as I was writing this it came to mind that we DO have a new syntactic form for private data coming in the form of private fields which use a hash prefix. It would be interesting to explore using the same syntax for module scoped private fields:
console.log(#dirname);
- Matthew Robb
On Sat, Aug 5, 2017 at 12:35 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
Too bad emails don’t have "thumbs up" and “+1”s :) So here’s my "+1” to you
On Sat, 05 Aug 2017 at 18:28 "T.J. Crowder"
<
">"T.J. Crowder"
wrote:
On Sat, Aug 5, 2017 at 5:05 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
So, in my opinion, the argument for not adding new global entities
such as System, or Module, or Loader (or heck, even all three of
them) being “these are not keywords, we can’t introduce them” is
really really weak.
Is anyone making that argument? I certainly am not. Not only is it possible to add more global entities, as you point out, it's been done repeatedly: Symbol
, Reflect
, etc. They just can't be keywords without breaking things. They have to be identifiers. Which means they have bindings with values. Which means those values can be copied. Which has implications.
On Sat, Aug 5, 2017 at 5:08 PM, Dmitrii Dimandt
< mailto:dmitrii at dmitriid.com
wrote:
That’s not what I was really aiming at :)
The original concern was “to get ‘module’ : 1. It's a
context-sensitive keyword, and code that's using it needs to
be updated when migrated to a module. “
I was just pointing out that ‘import’ is already a context-
sensitive keyword (as are a bunch of others, like super.
Is super a keyword BTW?)
My point was that this would be the only case I know of where it would be a keyword in one context but an identifier in another in the exact same production. super
, import
, etc., are always keywords. You just can't use them except in certain contexts. So I shouldn't have said "context-sensitive keyword" so much as "keyword or identifier depending on context." (But then...I did, earlier; I figured the shorthand was okay after spelling it out longhand. :-) )
But again: Maybe that's feasible. Or maybe it's not a problem passing the value around, in which case a predefined module
identifier only in module code isn't a problem anyway.
-- T.J. Crowder
This thread obviously hides both lots of almost-esotheric thought process, sometimes caught in remote discussions, and lots of emotions (and lots of blockquoted text - it helps readability immensely if you turn that off Dmitrii and not rely on mail/mailing-list clients to collapse it, thanks!).
If we put emotions aside, could someone just take 15-30 minutes and coherently dismantle Dmitrii's arguments? I believe he would like nothing more than to understand if andwhy his basis is dead-wrong and to understand the constraints that have lead us here, and the benefits that arise from import.meta new.target import import() etc design.
As it stands now, this thread reads more in favour of Dmitrii's logic to the naked eye, albeit with no winner except status-quo, and I fear that has to do partially with the fact that Dmitrii's replies have come with an abundance of context and references, while most of the feedback that he got was surgical in precision - pick something he said, give one argument against it, to which he provides a seemingly logical and well built counter argument, and the story repeats itself in multiple directions in parallel.
So once again - can someone take Dmitrii's main points and suggestions (e.g. Introspect, System, Module), which I think he summarized in this github issue tc39/ecma262/issues/968and just take them apart one by one, highlighting how his logic is either lacking context (e.g. you says apples and pears are different, when in fact the committee had this discussion last year and decided they will both be fruits) or is simply lacking soundness (e.g. you compare apples and pears, when in fact there's a subtle difference that may be hidden to the unfamiliar eye) ?
The community as a whole will thank you, no matter if as an individuals we may align or not.
Thank you advance, Andrei
PS: Disclaimer: I may be biased because I know Dmitrii in person, so I might be better at seeing past his apparently-harsh language and reading between his lines, but that doesn't stop me from having an untainted drive to digest argumentation logic and to understand what took the JavaScript language in what direction or another in general, and even more so on specific topics which are usually easier to grasp. I believe generations to come will benefit from such end-to-end argumentations in ways that we cannot even comprehend now.
PPS: I have been in the JavaScript world since the 6th grade, sometime in 1997, so no need to go easy on me. Those generations to come will be even more knowledged and critical than me, or us all put together, so lay it all out there without mercy. Shut Dmitrii up in one single post, with arguments that he cannot possibly counter.
I decided to do so myself: tc39/ecma262#968
I've been largely absent from this conversation because I would've found myself countering about 2/3 of the stuff in this thread (not just from Dmitrii), and the two TC39 members that've been significantly active in this thread didn't really do a great job explaining why he was wrong. (mostly missing context)
PSA for others: As for the incivility, please keep that crap off this list. I'm not pointing any fingers (unlike others), but please, keep this list respectful and mature.
Isiah Meadows me at isiahmeadows.com
Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com
What's wrong with having module
as an identifier simply meaning "this
module"?
I know it's special but is it really that remarkable?
Can anyone enlighten me as to how any input on features that are rushed into the standard works?
What is the purpose of hosting TC39 on GitHub if no input is expected from anybody but TC39 members?
Prime example: tc39/proposal-import-meta#2
Somehow it’s already in stage 2. Which means:
The committee expects to devote time to examining the problem space, solutions and cross-cutting concerns
Only reviews from committee members are expected, all other comments are locked out. If this makes it to stage 3 (and it will), it means:
The committee expects the feature to be developed and eventually included in the standard
So what’s the point of the whole process? Just shove whatever features you want/need onto the language and be done with it.
Regarding import.meta. Instead of properly speccing out and designing a Loader (whatwg.github.io/loader
), the import keyword was turned into a not-really-a-keyword-not-really-a-function abomination. It quickly reached stage 3. Any and all concerns by people who discovered this and voiced their concerns were dismissed with no argument, and dynamic import is now everywhere.
Now, since there has been no proper design of the feature, a
meta property
is just tacked onto the already confusing mess that isimport
. Expect it to reach stage 3 within a week or so, and then we are stuck with it forever.So, the question: why does TC39 even bother with the pretence of being transparent, o doing proper design on the language features etc.?