On Wed, Feb 19, 2014 at 11:56 AM, John Hann <jo...@unscriptable.com> wrote:
> I've been trying to use the spec *as is* and it's been very frustrating.
> It's not even close to being usable for anything but toy apps, imho. Maybe
> we should start a discussion about how we could help steer the TC39 team?
> Does anybody wish to debate the ES6 Loader spec here? Or should we do it
> somewhere else?
I agree. Any transpiler work has been of the trivial kind and a module system is really only usable when the module loader works. And AFAIK, to date, the loaders used by the transpilers have been AMD-based, or CJS.
I have tried to give private feedback to the ES module designers ...
A list of changes I would prefer to seeI’m skipping background descriptions and use case backup for these since I’m talking to an audience that should already have context:
1) Solve module inlining better. The committee feels this is a separable concern, that requires new network layer features, but there is a recognition it needs to exist via `loader.define(id, stringOfJavaScript)`.However, that is a horrible way to inline modules, and the other network layer stuff is still really speculative. It takes away time from people on committee from solving more of the actual module spec. Solve what people do today first. See about the speculative network layer changes like package URLs later.I gave specific proposals in this area, but they were discarded due to the inertia of past decisions/consensus.
2) The <module> HTML tag should die in a fire: the first script loaded likely needs to set up loader config, so it is a plain script, there is System.import for starting module loading, and better module inlining will address other inline module definition concerns. This is another example of effort that takes away from wrapping up modules and the module loader work.It also just looks like <newscript> tag: why not change more of the language if a new HTML tag is needed? (I don’t want that though) It also creates extra cognitive burden on any developer, using modules or not, when looking at the HTML docs for what tag to use for a script.
3) They need to have module-specific variable(s) to allow the module a module-specific loader.import() that supports relative ID resolution, and loading those modules in the same loader instance that loaded the current module. They also need to expose the equivalent of `module.id` and `module.uri`. Those id and url properties might have something in the spec soon for them, but it is not clear how they will solve the local loader issue yet.
4) Allow export default or named exports but not both. That would solve the issue that started this thread, and it would also allow `System.import(‘a’)` to just call a() if ‘a' exported a function as default. How it looks like now, you would have to do a.default() or some other named property, which is goofy, and indicates how `export default` was tacked on to the design later. This “one or the other” behavior is basically what AMD and CJS modules do now anyway, so a strong precedent for it.
5) The module loader needs a better way to load loader hooks. This is exactly what AMD loader plugins do: have a separator in the module ID to indicate the plugin module ID vs the plugin resource ID, and load the plugin, use its exports as the loader hooks for those kinds of IDs.
This will avoid a lot of footguns around people not overriding the existing ES loader hooks correctly, allows delayed loading and running of those hooks until there is a resource that needs them, and it would allow for the normalize and translate hooks to be synchronous. Or not if they want to be super flexible, keep those hooks async, but the other benefits still stand.This will likely get pushback because of 6) though.
6) Better baseline. This is likely the most contentious, and least likely to happen, so I put it last:There will likely be claims that different JS envs need the capability to be different and many concerns are just for the browser. However, what most developers want are consistency: the same system works everywhere.
The core behavior of module loading in the ES design is really driven by browser constraints anyway, so favor that as the default consistency.
It is fine to allow per JS-environment variations, to allow for example, node code to have the nested module lookup. That will be possible with the imperative overrides of loader hooks. However, using the browser constraints and basic declarative config would give a better baseline. That declarative config can live fine alongside the imperative overrides.
Other parts in this area:
* relative ID resolution should just be built in to the base loader instead of the base loader doing nothing.
* a basic, declarative config. It is important that this config is declarative (loader.config({})) instead of imperative (i.e., System.paths.foo = ‘’) to make it easier for build tools to find those calls and process them. It also allows that config to come from declarative file formats like JSON. The basic config in AMD loaders is a great place to start since it has had real world use.
* treat inlined modules as a core use case, not something just for the browser. Just as async loading of module bodies is treated as a core use case.* The loader plugin thing.This item #6 is probably the biggest fundamental issue that has colored my feedback and its reception. So be aware of any base line premises or design goals that are driving your feedback. If they do not match with who is designing ES modules, it will make it hard to communicate effectively.
....
Anyway, if you want to give feedback to them, maybe the above will enable you to be more successful.
An important correction: I received a private note indicating that the ES designers did not ignore the feedback, they just could not respond to it due to length and mixing of concerns. I got the impression from a different response that a good chunk of it was just ignored.I apologize for the mischaracterization. They are all trying hard and doing research into existing systems. It has felt opaque and hard to understand the roots of some decisions. As an implementer I like more details, and I felt there was not a full understanding about existing module uses. The ES designers see it differently, they feel like they have put in significant work to understand.Some notes inline:On Mon, Mar 3, 2014 at 5:56 PM, Guy Bedford <guybe...@gmail.com> wrote:
For module inlining, myself and John Barton have been working on a `System.register` inlining method, which would work analogously to the named defines in AMD, while remaining CSP compatible and not dealing with string sources.Maybe that will work for AMD/CJS code upconverted to work inside an ES module loader, but that will not work for ES modules because of the way that mutable slots via `import` work: they are very difficult to emulate in existing code. I thought a little bit about it in a throw-away experiment I did (you can ignore most of the rest of it):but really if language keywords are used for module syntax, inlining needs something similar. I did some sketching of that here, but note that is just a sketch to talk about the issues involved, nothing real:Technical details aside, it fits quite neatly into the existing loader pipeline allowing for bundles (https://github.com/systemjs/systemjs/blob/master/lib/system-bundles.js#L11).There are currently two key issues I have with the spec at the moment which are:1. Circular references - providing support for CommonJS-style circular reference handlingThe mutable slots from `import` is to allow the module to have a working reference to an export even though it may not actually have the exported value yet. The CJS style circular referencing should not be needed, and I do not believe cannot be supported natively in an ES loader for ES modules.This is one of the big things the CJS/node approach got incorrect: expecting a sync require() call to fetch and execute at the point of the call is not realistic to support in the browser. I do not think, or expect the ES system to go to incredible measures to make up for that choice, but leave it for node to use the hooks into the ES loader API to provide a require() that can load legacy node modules in that matter, probably using System.set() to jam in the legacy exports into the module loader (if they make them visible at all).2. Deferred execution - currently ES6 modules defer their execution until they are loaded. But AMD and CommonJS modules in the ES6 loader don't. System.register would suffer from this - I really think they should get the same treatment.Maybe I am unsure what you mean by defer execution, but AMD loader should definitely not execute the factory function until that module is part of a top level require([]) dependency chain. I believe it aligns well with the ES model. Maybe you mean that the instantiate hook needs more specification. CJS behavior in this case seems undefined, as they are always synchronously loaded and executed, they did not support a use case of being loaded but not executed.James
> I'd like 'default' to die, but it seems that it was added for the benefit of AMD/CJS ;-)This is not funny :( If `default` has been added for the benefit of AMD/CJS and now AMD/CJS people are having trouble dealing with this feature (e.g this thread, or https://github.com/square/es6-module-transpiler/issues/85) - something is very wrong. Seems like misdesign or miscommunication that lead to misdesign?
--
You received this message because you are subscribed to the Google Groups "amd-implement" group.
To unsubscribe from this group and stop receiving emails from it, send an email to amd-implemen...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
On 4 March 2014 01:25, Karolis Narkevicius <karo...@gmail.com> wrote:> I'd like 'default' to die, but it seems that it was added for the benefit of AMD/CJS ;-)This is not funny :( If `default` has been added for the benefit of AMD/CJS and now AMD/CJS people are having trouble dealing with this feature (e.g this thread, or https://github.com/square/es6-module-transpiler/issues/85) - something is very wrong. Seems like misdesign or miscommunication that lead to misdesign?This thread was initially started to share the transpile output that solves #85. We have now integrated this fix into the Traceur AMD compilation output, so that interop DOES work.To summarise, AMD modules are imported by the default property:import $ from 'jquery';The above ES6 can be transpiled into AMD and work correctly for loading jQuery when jQuery is an AMD module, both in ES6 loaders like SystemJS and in existing AMD loaders today.Sharing the interoperability solution was my primary goal with this thread. It seems I failed to adequately communicate that!
jjb
An important correction: I received a private note indicating that the ES designers did not ignore the feedback, they just could not respond to it due to length and mixing of concerns. I got the impression from a different response that a good chunk of it was just ignored.I apologize for the mischaracterization. They are all trying hard and doing research into existing systems. It has felt opaque and hard to understand the roots of some decisions. As an implementer I like more details, and I felt there was not a full understanding about existing module uses. The ES designers see it differently, they feel like they have put in significant work to understand.
Some notes inline:For module inlining, myself and John Barton have been working on a `System.register` inlining method, which would work analogously to the named defines in AMD, while remaining CSP compatible and not dealing with string sources.Maybe that will work for AMD/CJS code upconverted to work inside an ES module loader, but that will not work for ES modules because of the way that mutable slots via `import` work: they are very difficult to emulate in existing code. I thought a little bit about it in a throw-away experiment I did (you can ignore most of the rest of it):
but really if language keywords are used for module syntax, inlining needs something similar. I did some sketching of that here, but note that is just a sketch to talk about the issues involved, nothing real:
Technical details aside, it fits quite neatly into the existing loader pipeline allowing for bundles (https://github.com/systemjs/systemjs/blob/master/lib/system-bundles.js#L11).There are currently two key issues I have with the spec at the moment which are:1. Circular references - providing support for CommonJS-style circular reference handlingThe mutable slots from `import` is to allow the module to have a working reference to an export even though it may not actually have the exported value yet. The CJS style circular referencing should not be needed, and I do not believe cannot be supported natively in an ES loader for ES modules.This is one of the big things the CJS/node approach got incorrect: expecting a sync require() call to fetch and execute at the point of the call is not realistic to support in the browser. I do not think, or expect the ES system to go to incredible measures to make up for that choice, but leave it for node to use the hooks into the ES loader API to provide a require() that can load legacy node modules in that matter, probably using System.set() to jam in the legacy exports into the module loader (if they make them visible at all).
2. Deferred execution - currently ES6 modules defer their execution until they are loaded. But AMD and CommonJS modules in the ES6 loader don't. System.register would suffer from this - I really think they should get the same treatment.Maybe I am unsure what you mean by defer execution, but AMD loader should definitely not execute the factory function until that module is part of a top level require([]) dependency chain. I believe it aligns well with the ES model. Maybe you mean that the instantiate hook needs more specification. CJS behavior in this case seems undefined, as they are always synchronously loaded and executed, they did not support a use case of being loaded but not executed.
James
But, if instead of ES6, I had written AMD in the above:<script type="module" name="my-module">define(function() {console.log('executed');return { some: 'export' };});</script>The execution will happen as soon as that tag is run (via `System.define`).This difference in execution between native ES6 and other module types seems entirely unnecessary to me, but it is quite a subtle point I understand.
But, if instead of ES6, I had written AMD in the above:<script type="module" name="my-module">define(function() {console.log('executed');return { some: 'export' };});</script>The execution will happen as soon as that tag is run (via `System.define`).This difference in execution between native ES6 and other module types seems entirely unnecessary to me, but it is quite a subtle point I understand.
On Tuesday, March 4, 2014 6:31:05 PM UTC-6, Guy Bedford wrote:But, if instead of ES6, I had written AMD in the above:<script type="module" name="my-module">define(function() {console.log('executed');return { some: 'export' };});</script>The execution will happen as soon as that tag is run (via `System.define`).This difference in execution between native ES6 and other module types seems entirely unnecessary to me, but it is quite a subtle point I understand.to me, this looks to be desirable. `define` is executed immediately and then the execution of the factory is deferred until something else depends on this module. if `define` is not executed immediately, how/when do you know what dependencies need to be fulfilled before the module's factory can be executed?
also, with circular dependencies, where is the problem? if AMD has this solved today (which it has solved via the "exports" dependency) and a compatibility layer can load AMD via an ES6 module loader then what is missing? can't the AMD compatibility layer manage the circular dependencies just as AMD loaders do today? i would assume that if the compatibility layer is controlling the execution of the factory (NOT the module i.e. the call to define, but the factory provided in the call to define) then it should have everything necessary to handle circular dependencies - it can defer the execution of the factory and provide the objects passed to the factory. isn't that all that's necessary to continue to handle circular dependencies in the same way they are handled today?
thanks,ben...
i think i understand a little better now. so, if ES6 would allow for a compatibility layer to execute an AMD module (define is called) and then at a later time have the compatibility layer provide the module's value (the factory is executed) then circular dependencies and bundling could work. is that right?
--
Unfortunately, the current Loader spec needs to be thrown out, and a new one needs to be started. I have no idea how to approach this without alienating (aka "seriously pissing off") the TC39 folks. My current plan is to use hard evidence to show them that there are flaws (hopefully gaining some credibility while I do that) and then to find ways to influence them in the right direction. I've got some ideas about which flaws to present first, but it's going to be hard to get them to throw out the work they've done so far.If anybody else has a great plan to start from scratch, I'm all ears.
I am super burned out on this though, and for my own sanity expect to disengage from discourse about modules and loaders for a while and just focus on my own code and work priorities to recharge.
James
James