Re: ModuleImport
On Thu, Jul 3, 2014 at 9:05 PM, Brendan Eich bren...@mozilla.org wrote: Jussi Kalliokoski wrote: So sometimes someone can need it, so we must have good support? Is that how we operate these days? Cool down a minute :-|. Heh, the internet is a funny place when it comes to interpreting emotion; I've actually been very calm the whole time. ;D JS is a mature language on a big rich-and-messy evolving platform-set (browser JS, Node.js, other embeddings). We don't preach only majority use cases or there should be only one way to do it -- more TIMTOWDI or TimToady Bicarbonate: http://en.wikipedia.org/wiki/There%27s_more_than_one_way_to_do_it JS systems start small and grow. Modules often merge, split, merge again. Cycles happen in the large. ES6 modules address them, they were always a design goal among several goals. I'm well aware, to me it looks like cycles are the defining feature of the module system on which other features have been built on. The reason this is a problem is because the requirement of cyclic dependencies not only complicates the API surface, reasoning about it and implementations, it also excludes a lot of features. For example, if we look at the problem of optional dependencies (which, btw, unlike cyclic dependencies are an extremely common corner case, especially on the web, and can't really be refactored away) is not that you can't load things dynamically, because you can, but in the fact that importing a module is async but initializing and declaring is not, it's static to support mutable bindings magic (that are required for transitive cyclic dependencies) and compile-time errors. Now, let's have a hypothetical change to the module system. Let's say that we allow only exporting one thing, and that one thing can be any value. When you import it, it's like assigning a variable, except that resolving the value you are assigning to is done async at compile time. Like: // somewhere.js export { something: function something () {} }; // doSomethingElse.js import { something } from somewhere; export function doSomethingElse () {}; Benefit #1: No module meta object crap, the only new concept needed to understand this is compile-time prefetching. Benefit #2: No special destructuring syntax (since you're doing normal destructuring on a normal value). Cool, but we broke cyclic dependencies without fixing optional dependencies: // optional dependency here var fasterAdd = System.import(fasterAdd); var basicAdd = function (a, b) { return a + b; }; // because there's no async initialize, we have to impose an async interface for an otherwise sync operation export function add (a, b) { return fasterAdd .catch( = basicAdd ) .then( (addMethod) = addMethod(a, b) ); }; However, with this design, we can allow exporting a promise of what we want to export, thus deferring the import process until that promise is resolved or rejected: var basicAdd = function (a, b) { return a + b; }; export System.import(fasterAdd) .catch( = basicAdd ); And there you have it, voilá. Benefit #3: See #1, if you don't want to, you don't even have to comprehend compile-time prefetching anymore. It's just optimization sugar. Benefit #4: No need to impose async interfaces for inherently sync operations just because the initialize phase is not async while loading is. Benefit #5: You can do stuff like async feature detects in the initialize phase, something that is completely broken in existing module systems (you *can* do this with RequireJS through some effort, but I'm not sure it's officially supported or part of AMD). Benefit #6: High compatibility with existing module systems, including edge cases, providing a solid foundation for transpiling legacy modules to the new system. See some sketches I made yesterday when playing around with the idea: https://gist.github.com/jussi-kalliokoski/6e0bf476760d254e5465 (includes an example of how you could implement localforage if the platform dependencies were provided as modules). Benefit #7: Addresses the issue of libraries depending on more than just JS: import {loadImages, importStylesheets} from fancy-loader; var myModule = { ... }; export Promise.all([ loadImages([foo.jpg, bar.png]), importStylesheets([style.css]) ]).then( = myModule ); There's probably more that didn't come to my mind. So, this would support pretty much all the features of the existing solutions and more. Just not cyclic dependencies. At all. Now, we could of course try to add this as an afterthought to the current design by letting individual exports be promises, but in order to preserve transitive cyclic dependencies in that case, you'd have to wait for the promise of that to resolve when the importing module accesses the imported binding, not only causing execution to yield to the event loop unexpectedly (which is what we're trying to avoid with async functions) but also makes it transitive only most of the time, ending up with cyclic dependencies that
Re: ModuleImport
On Fri, Jul 4, 2014 at 10:19 AM, Jussi Kalliokoski jussi.kallioko...@gmail.com wrote: On Thu, Jul 3, 2014 at 9:05 PM, Brendan Eich bren...@mozilla.org wrote: Cool down a minute :-|. I now realize that my tone on this thread hasn't been very considerate, and apologize if I offended anyone, or even if I didn't. I don't want anyone to think that I don't respect the work that's been done to get ES6 modules where they are today. Considering the use cases and requirements they've been designed for, the design is actually great. My goal was only to challenge those use cases and requirements and their priority over other ones, but I failed at conveying that properly. Sorry. Cheers, Jussi ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
On Thu, Jul 3, 2014 at 1:29 AM, Brian Di Palma off...@gmail.com wrote: The arguments for and against supporting cyclic dependencies seem to be academic. I'm yet to see any evidence of their importance in practice nor proof they they are fundamental ... or not. Transitive cyclic dependencies. I'd say that's the case that was in the minds of the authors of the module system. In large codebases those can happen and a module system that does not handle them gracefully would be poor. Support for them is needed, and what CommonJS has is not good enough. They are acknowledged in the modules documentation for node http://nodejs.org/api/all.html#all_cycles This does not mean they are recommended, the same holds true for ES6 modules. It is an acceptance of the reality of complex and large codebases that sometimes cyclic dependencies can occur. So sometimes someone can need it, so we must have good support? Is that how we operate these days? It boils down to this. You can import a dependency in three ways import MyClass from 'MyClass'; import {MyClass} from 'MyClass'; module myClass from 'MyClass'; And (in the same order): System.import(MyClass).then(function (MyClassModule) { // I don't actually know how someone would even access the default exports from the module object unless in case of default exports there is no module object, just the default exports as the module. var MyClass = MyClassModule; }); System.import(MyClass).then(function (MyClassModule) { var { MyClass } = MyClassModule; }); System.import(MyClass).then(function (MyClassModule) { var myClass = MyClassModule; }); var MyClass = System.get(MyClass); var {MyClass} = System.get(MyClass); var myClass = System.get(MyClass); That's one too many ways for the simplest module system that fulfills all requirements. import MyClass from 'MyClass'; import {MyClass} from 'MyClass'; import * as myClass from 'MyClass'; Is not the fix. The confusion stemmed from the first production not the last. I agree. Thanks, I'm actually no longer even indifferent towards default exports but I also think it should go. Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. B. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
So sometimes someone can need it, so we must have good support? Is that how we operate these days? Imagine a large codebase which already has transitive cyclic dependencies. If the module system has poor support for them it might still work with them until one day a developer reordered the import statements. How would you feel if such a simple operation caused you issues? Or upgrading to the latest version of a popular utility toolchain like lo-dash could introduce an issue purely because the upgrade created a transitive cyclic dependency. And the fix for that would be to reorder your import statements and add comments in your module telling people not to change the order. Again, how would you feel? TC39 has decided to spare us all those special moments by including good cyclic dependency support. // I don't actually know how someone would even access the default exports from the module object Like so, ``` System.import(MyClass) .then(function (myClassModule) { myClassModule.default }); ``` Default import and exports are purely sugar over ``` import {default as MyClass} from 'MyClass'; ``` It saves you a few character typing out when importing from legacy module system. ``` import MyClass from 'MyClass'; ``` Those two are the same thing. From birth the brand new module system is going to have this superfluous appendage to support module systems that 10 years from now people will struggle to remember. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
On Thu, Jul 3, 2014 at 11:41 AM, Brian Di Palma off...@gmail.com wrote: So sometimes someone can need it, so we must have good support? Is that how we operate these days? Imagine a large codebase which already has transitive cyclic dependencies. If the module system has poor support for them it might still work with them until one day a developer reordered the import statements. How would you feel if such a simple operation caused you issues? Happy; finally having a good motivator to refactor and get rid of the cyclic dependencies, at least in that compartment. Or upgrading to the latest version of a popular utility toolchain like lo-dash could introduce an issue purely because the upgrade created a transitive cyclic dependency. And the fix for that would be to reorder your import statements and add comments in your module telling people not to change the order. Again, how would you feel? Angry at lo-dash for breaking backwards compatibility, then I'd revert the upgrade and file a bug against it. I'd probably also consider whether lo-dash were in this imaginary case something I want to depend on given that for a utility library they need cyclic dependencies. I'd also be happy that the module system made this obvious. But I get your point since I'm well aware that my thoughts are likely not to be a very good representation of how most people would feel. However, I still don't think it's something worth making other sacrifices over. Default import and exports are purely sugar over ``` import {default as MyClass} from 'MyClass'; ``` It saves you a few character typing out when importing from legacy module system. ``` import MyClass from 'MyClass'; ``` Those two are the same thing. Ughh, I see. Not cool. From birth the brand new module system is going to have this superfluous appendage to support Let's not have it. At least not in ES6. Tools like Traceur can support it for an easier migration path since they already have diverged from ES.next anyway with all the annotations (for which, off topic, I haven't seen even a proposal here yet) and stuff. module systems that 10 years from now people will struggle to remember. I certainly hope that will be the case, but it's 2014 and people are still implementing banking with Cobol, security protocols with C++ and website cryptography with Java applets. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
On Thu, Jul 3, 2014 at 2:31 AM, Jussi Kalliokoski jussi.kallioko...@gmail.com wrote: Tools like Traceur can support it for an easier migration path since they already have diverged from ES.next anyway with all the annotations (for which, off topic, I haven't seen even a proposal here yet) and stuff. Jussi, I would appreciate a bug report on the Traceur github project pointing to information that makes you think this statement is correct. We consider divergence from ES.next to be a bug and do not support any feature outside of the proposal from TC39. Our project does provides great technology that has been used to develop migration tools, annotation experiments, and stuff. All of that comes on other projects or under opt-in flags on traceur. jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
On Thu, Jul 3, 2014 at 5:42 PM, John Barton johnjbar...@google.com wrote: On Thu, Jul 3, 2014 at 2:31 AM, Jussi Kalliokoski jussi.kallioko...@gmail.com wrote: Tools like Traceur can support it for an easier migration path since they already have diverged from ES.next anyway with all the annotations (for which, off topic, I haven't seen even a proposal here yet) and stuff. Jussi, I would appreciate a bug report on the Traceur github project pointing to information that makes you think this statement is correct. We consider divergence from ES.next to be a bug and do not support any feature outside of the proposal from TC39. Annotations are marked as experimental, but I filed a bug anyway to either get a proposal or update the wiki to inform that there's no official spec or proposal for it: https://github.com/google/traceur-compiler/issues/1156 ;) Our project does provides great technology that has been used to develop migration tools, annotation experiments, and stuff. All of that comes on other projects or under opt-in flags on traceur. Exactly, and that's how it could be done in traceur. - Jussi jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Tue, Jul 1, 2014 at 10:28 PM, Kevin Smith zenpars...@gmail.com wrote: As such, we are balancing the marginal user experience gains of export-overwriting against the better support for circular dependencies of real modules. Another way of looking at it: Being in control of the language, you can always invent new sugar to optimize user experience (within limits) when real usage data proves its worth. But the core model can't be changed. As such, it seems like it would be better to err on the side of making the core model simple and solid, leaving minor UX optimizations to future syntax. But it's neither simple nor solid. It's overtly complicated to support features that shouldn't be there. Sorry in advance for the tone of this message, it is quite negative. But the intent is constructive. To me modules are the most anticipated feature in ES6 and I've been closely following the discussion and the proposal's evolution and I've been extremely thrilled. Finally we could have a chance of alleviating the situation of having a ton of non-intercompatible different module loaders. I haven't contributed much to the discussion since I liked the overall direction. However, now that I've been actually using the modules for a while with a couple of different transpilers, it's become obvious that the design is inherently broken (not the fault of the transpilers, they are actually probably better in some aspects than the real thing), and in order for ES modules to help the situation instead of making it worse, it needs to be better than the existing solutions. The core unique selling points of ES6 modules as opposed to the other module loading systems: * Language built-in: the strongest point. This makes sure that most tooling should support the feature out of the box and that module authors are more inclined to provide their modules in this format. But the design can be whatever and this point will still hold true, so no points to the design here. * (Reliably) statically analyzable syntax. This is my favourite feature and really awesome. Allows engine to fetch next modules while the current one is still being parsed, and tooling to better understand what the code does. However, this would hold true even if all we standardized was syntactic sugar on top of requirejs. * Cyclic dependencies: First of all, this is not a feature you want to necessarily encourage. It looks good in academia, but in my career I've yet to see real world code that would benefit more from cyclic dependencies more than refactoring. Not to mention that having cyclic dependencies have been possible in global scope modules since LiveScript, and is possible in CommonJS as well if you do a little DI: https://gist.github.com/jussi-kalliokoski/50cc79951a59945c17a2 (I had such a hard time coming up with an example that could use cyclic dependencies that I had to dig the example from modules examples wiki). And honestly I don't think it's a feature that deserves to be easier to do than my example, and especially not worth making other design compromises for. * Mutable bindings: This will make a good chapter in a future edition of JavaScript: The bad parts, along with being able to redefine undefined. You can have mutable bindings in other module systems as well, just reassign something in your exports and then your consumer uses the exports property directly. A lot of WTFs avoided and even doing it like that will ring alarm bells in code review. * Compile-time errors: This not a feature, it's a bug. Try finding a website that doesn't somewhere in its code check for whether you have a feature / module available, i.e. optional dependencies. Some examples: Angular has jQuery as an optional dependency; spritesheet generator modules for node that have multiple engines as optional dependencies because some of them may be native modules that don't compile on all platforms. Also things get worse if platform features are implemented as modules. Let's say things like localStorage, IndexedDB and friends were provided as modules and as a result, things like localforage would either not exist or would be infinitely more complex. Just look at github search for keywords `try` and `require` https://github.com/search?l=javascriptq=try+requireref=cmdformtype=Code to see how widely used the pattern of wrapping a module load in a try-catch is. Now let's look at some things that tip the odds into existing solutions favor: * Massive amount of existing modules. * Existing large-scale user-bases. * Node has stated that the core will always be CommonJS, meaning that on node, in order to use ES6 modules, you'll have to be using two different module systems which doesn't sound like a thing that people would do unless there's proven benefits. * Completely dynamic. Now, I know there are people that think that this isn't not good, but it is. It gives you a lot of power when debugging things or playing around with new things (something I haven't seen discussed
Re: ModuleImport
But it's neither simple nor solid. It's overtly complicated to support features that shouldn't be there. I have to disagree here. If we drop default imports, then we can describe the module system like this: Variables can be exported by name. Variables can be imported by name. It doesn't get any more simple than that. What I mean by solid is that it has good coverage of the edge cases, meaning primarily cyclic dependencies. Sorry in advance for the tone of this message, it is quite negative. I didn't perceive this as negative. I think it's quite constructive to uncover all of the arguments. * Massive amount of existing modules. * Existing large-scale user-bases. We've already taken a look at the technical side of interoperability with old-style modules, and there's no problem there. What remains, I suppose, is a sociological argument. More on that later. * Node has stated that the core will always be CommonJS, meaning that on node, in order to use ES6 modules, you'll have to be using two different module systems which doesn't sound like a thing that people would do unless there's proven benefits. If users want Node core to be exposed as ES6 modules, then the Node developers will provide it. It's not some ideological battle - it's about whatever is good for the platform. Regarding two module systems at the same time: more later. * Completely dynamic. Now, I know there are people that think that this isn't not good, but it is. It gives you a lot of power when debugging things or playing around with new things (something I haven't seen discussed re:modules on this list). One of the greatest things in JS is that instead of reading the usually poor documentation, let alone the code, of something you want to use you can just load it up in node or the developer tools and play around with it. With node, you require() something in the repl and you see immediately what it exports. (...edit...) This is simply not possible with ES6 modules without a massive boilerplate spaghetti with async stuff. You're right, but I don't think we need objects-as-modules to address this. We want a blocking load API for these situations: 1. Any REPL (strong) 2. Server-only programs which don't care about async loading and don't want the complications (weaker) In es6now, I provide a `loadModule` function for loading ES6 modules synchronously in the REPL. I think Node would want to provide a synchronous loading method as part of the so-called module meta object. That API needs eyes, BTW. Given all this, how are we supposed to convince people to use this stuff? These concerns are not something that can be fixed later either, they're fundamental to the current design. I don't see any technical problem here. So let's look at the sociological argument: ES6 modules are different from Node/AMD modules, and seem to be at odds philosophically. Since Node/AMD modules already have established user bases, and important members of the Node community are critical, ES6 modules won't be successful at penetrating the market. Counter-argument: Take a look at this package: https://github.com/zenparsing/zen-sh . It's an experimental ES6 package which allows you to open up a shell and execute commands using tagged template strings. I use it at work to automate git tasks. It's awesome (but very rough). It's completely installable using NPM, today. I encourage anyone to try it out (you'll need to install es6now https://github.com/zenparsing/es6now first, though). It exports a single thing, but that thing is given a name. It is set up to work with both `require` and `import`. Now, the sociological argument says that because it's written as an ES6 module the community will reject this package. Does that sound plausible to you? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
circular dependencies in CJS are much easer then that https://gist.github.com/calvinmetcalf/57be20b8eda0ee8fe6de On Wed, Jul 2, 2014 at 4:09 AM, Jussi Kalliokoski jussi.kallioko...@gmail.com wrote: On Tue, Jul 1, 2014 at 10:28 PM, Kevin Smith zenpars...@gmail.com wrote: As such, we are balancing the marginal user experience gains of export-overwriting against the better support for circular dependencies of real modules. Another way of looking at it: Being in control of the language, you can always invent new sugar to optimize user experience (within limits) when real usage data proves its worth. But the core model can't be changed. As such, it seems like it would be better to err on the side of making the core model simple and solid, leaving minor UX optimizations to future syntax. But it's neither simple nor solid. It's overtly complicated to support features that shouldn't be there. Sorry in advance for the tone of this message, it is quite negative. But the intent is constructive. To me modules are the most anticipated feature in ES6 and I've been closely following the discussion and the proposal's evolution and I've been extremely thrilled. Finally we could have a chance of alleviating the situation of having a ton of non-intercompatible different module loaders. I haven't contributed much to the discussion since I liked the overall direction. However, now that I've been actually using the modules for a while with a couple of different transpilers, it's become obvious that the design is inherently broken (not the fault of the transpilers, they are actually probably better in some aspects than the real thing), and in order for ES modules to help the situation instead of making it worse, it needs to be better than the existing solutions. The core unique selling points of ES6 modules as opposed to the other module loading systems: * Language built-in: the strongest point. This makes sure that most tooling should support the feature out of the box and that module authors are more inclined to provide their modules in this format. But the design can be whatever and this point will still hold true, so no points to the design here. * (Reliably) statically analyzable syntax. This is my favourite feature and really awesome. Allows engine to fetch next modules while the current one is still being parsed, and tooling to better understand what the code does. However, this would hold true even if all we standardized was syntactic sugar on top of requirejs. * Cyclic dependencies: First of all, this is not a feature you want to necessarily encourage. It looks good in academia, but in my career I've yet to see real world code that would benefit more from cyclic dependencies more than refactoring. Not to mention that having cyclic dependencies have been possible in global scope modules since LiveScript, and is possible in CommonJS as well if you do a little DI: https://gist.github.com/jussi-kalliokoski/50cc79951a59945c17a2 (I had such a hard time coming up with an example that could use cyclic dependencies that I had to dig the example from modules examples wiki). And honestly I don't think it's a feature that deserves to be easier to do than my example, and especially not worth making other design compromises for. * Mutable bindings: This will make a good chapter in a future edition of JavaScript: The bad parts, along with being able to redefine undefined. You can have mutable bindings in other module systems as well, just reassign something in your exports and then your consumer uses the exports property directly. A lot of WTFs avoided and even doing it like that will ring alarm bells in code review. * Compile-time errors: This not a feature, it's a bug. Try finding a website that doesn't somewhere in its code check for whether you have a feature / module available, i.e. optional dependencies. Some examples: Angular has jQuery as an optional dependency; spritesheet generator modules for node that have multiple engines as optional dependencies because some of them may be native modules that don't compile on all platforms. Also things get worse if platform features are implemented as modules. Let's say things like localStorage, IndexedDB and friends were provided as modules and as a result, things like localforage would either not exist or would be infinitely more complex. Just look at github search for keywords `try` and `require` https://github.com/search?l=javascriptq=try+requireref=cmdformtype=Code to see how widely used the pattern of wrapping a module load in a try-catch is. Now let's look at some things that tip the odds into existing solutions favor: * Massive amount of existing modules. * Existing large-scale user-bases. * Node has stated that the core will always be CommonJS, meaning that on node, in order to use ES6 modules, you'll have to be using two different module systems which doesn't sound like a thing that
Re: ModuleImport
circular dependencies in CJS are much easer then that https://gist.github.com/calvinmetcalf/57be20b8eda0ee8fe6de Umm... I would rather call that tricky as hell ; ) ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
circular dependencies in CJS are much easer then that https://gist.github.com/calvinmetcalf/57be20b8eda0ee8fe6de Umm... I would rather call that tricky as hell ; ) Besides, it fails if you do `require(./b)`, IIUC. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
I agree with everything Jussi wrote. He gets to the heart of the issue. The points that have been previously made about bundling modules also apply to REPLs. If a module must be in a file, I can't easily define modules at a REPL prompt. --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
The arguments for and against supporting cyclic dependencies seem to be academic. I'm yet to see any evidence of their importance in practice nor proof they they are fundamental ... or not. I wouldn't say that cyclic dependencies are academic, in the sense of not real. If they are allowed by the language, they will be used. I see them in Python code and I see them in Node modules. So unless you plan to disallow them altogether, it makes sense to provide good support, all other things being equal. It's an edge case for the module system, to be sure, but we should expect the module system to have reasonable coverage over the edge cases. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Wed, Jul 2, 2014 at 7:09 PM, John Barton johnjbar...@google.com wrote: * (Reliably) statically analyzable syntax. This is my favourite feature and really awesome. Allows engine to fetch next modules while the current one is still being parsed, This isn't true -- all module designs in play require parsing (include of course CJS and AMD). Huh? I wasn't saying that they don't. I mean that with the static syntax the parser can initiate the request immediately when it hits an import statement, which is a good thing and not possible with what is out there. You can of course assume that the require call with a static string does what you'd expect but then you might end up loading something that was never actually required but someone had their own require function there instead that has something else. and tooling to better understand what the code does. However, this would hold true even if all we standardized was syntactic sugar on top of requirejs. I don't believe that anyone expects such an outcome. Heh of course not, that would be horrible; I was referring to the fact that this is a low-hanging fruit to pick. * Cyclic dependencies: First of all, this is not a feature you want to necessarily encourage. It looks good in academia, but in my career I've yet to see real world code that would benefit more from cyclic dependencies more than refactoring. Not to mention that having cyclic dependencies have been possible in global scope modules since LiveScript, and is possible in CommonJS as well if you do a little DI: https://gist.github.com/jussi-kalliokoski/50cc79951a59945c17a2 (I had such a hard time coming up with an example that could use cyclic dependencies that I had to dig the example from modules examples wiki). And honestly I don't think it's a feature that deserves to be easier to do than my example, and especially not worth making other design compromises for. The arguments for and against supporting cyclic dependencies seem to be academic. I'm yet to see any evidence of their importance in practice nor proof they they are fundamental ... or not. True, and that being the case I don't see the reason of putting them on a pedestal. If they happen to be a nice side effect, that's fine, but I'm mostly referring to arguments against different proposals using doesn't support cyclic dependencies. * Compile-time errors: This not a feature, it's a bug. Try finding a website that doesn't somewhere in its code check for whether you have a feature / module available, i.e. optional dependencies. Some examples: Angular has jQuery as an optional dependency; spritesheet generator modules for node that have multiple engines as optional dependencies because some of them may be native modules that don't compile on all platforms. Also things get worse if platform features are implemented as modules. Let's say things like localStorage, IndexedDB and friends were provided as modules and as a result, things like localforage would either not exist or would be infinitely more complex. Just look at github search for keywords `try` and `require` https://github.com/search?l=javascriptq=try+requireref=cmdformtype=Code to see how widely used the pattern of wrapping a module load in a try-catch is. Optional dependency is completely supported by Loader.import(). Furthermore its promise based API avoids try/catch goop. Try/catch is far less goop than promises, and furthermore your non-optional dependencies don't come in as promises, and neither can you define your module asynchronously and wait to see whether the optional dependency is available before exposing your interface. If you could define your module like this it would be less of a problem but still ugly and inferior (in this specific case) to for example CJS: import someRequiredDependency from somewhere; Loader.import(someOptionalDependency) .catch(function noop () {}) .then(function (someRequiredDependency) { exports function doSomething (foo) { if ( someOptionalDependency ) { return someOptionalDependency(foo + 5); } else { return someRequiredDependency(foo + 2); } }; }); Now let's look at some things that tip the odds into existing solutions favor: * Massive amount of existing modules. * Existing large-scale user-bases. * Node has stated that the core will always be CommonJS, meaning that on node, in order to use ES6 modules, you'll have to be using two different module systems which doesn't sound like a thing that people would do unless there's proven benefits. These points are not relevant since nothing in the current design prevents these success stories from continuing. The two first points are relevant if they decrease the chances of ES6 modules becoming the most used module system (which is obviously a goal because otherwise we'll just be making things worse by contributing to fragmentation). The last one is relevant because it
Re: ModuleImport
You can of course assume that the require call with a static string does what you'd expect but then you might end up loading something that was never actually required but someone had their own require function there instead that has something else. This is a solved problem actually https://github.com/calvinmetcalf/derequire ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
This seems like a bit too many issues, so let me just correct one (important) one. On Wed, Jul 2, 2014 at 2:09 PM, Jussi Kalliokoski jussi.kallioko...@gmail.com wrote: On Wed, Jul 2, 2014 at 7:09 PM, John Barton johnjbar...@google.com wrote: Now, I know there are people that think that this isn't not good, but it is. It gives you a lot of power when debugging things or playing around with new things (something I haven't seen discussed re:modules on this list). One of the greatest things in JS is that instead of reading the usually poor documentation, let alone the code, of something you want to use you can just load it up in node or the developer tools and play around with it. With node, you require() something in the repl and you see immediately what it exports. Loader.get() provides the module. Hmm, my bad, I actually thought that Loader.get() works only when the module has already been fetched. Your thought was correct: Loader.get() only works if the module is fetched. It was my impression that you were describing a debugging scenario where the module would be loaded and where you are likely to want to avoid module-loading since your goal is to debug the live image. Well that improves things a lot but that still leaves the disparity between what you'd write in actual code and the repl and thus fails to be better (in this case) than for example CommonJS. I expect devtools to support declarative import in their REPL, so the code you would write is the same. jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Wed, Jul 2, 2014 at 3:38 PM, Kevin Smith zenpars...@gmail.com wrote: But it's neither simple nor solid. It's overtly complicated to support features that shouldn't be there. I have to disagree here. If we drop default imports, then we can describe the module system like this: Variables can be exported by name. Variables can be imported by name. FWIW, I don't have a problem with dropping / deferring default exports, although my personal ideal is that like functions, modules should do one thing and thus export one thing, but having no default exports doesn't prevent exporting just one thing. It doesn't get any more simple than that. What I mean by solid is that it has good coverage of the edge cases, meaning primarily cyclic dependencies. The complexity is in having multiple different ways of doing one thing and introducing new kind of bindings to the language that didn't exist before to support those edge cases. Sorry in advance for the tone of this message, it is quite negative. I didn't perceive this as negative. I think it's quite constructive to uncover all of the arguments. Happy to hear! * Massive amount of existing modules. * Existing large-scale user-bases. We've already taken a look at the technical side of interoperability with old-style modules, and there's no problem there. What remains, I suppose, is a sociological argument. More on that later. * Node has stated that the core will always be CommonJS, meaning that on node, in order to use ES6 modules, you'll have to be using two different module systems which doesn't sound like a thing that people would do unless there's proven benefits. If users want Node core to be exposed as ES6 modules, then the Node developers will provide it. It's not some ideological battle - it's about whatever is good for the platform. Regarding two module systems at the same time: more later. * Completely dynamic. Now, I know there are people that think that this isn't not good, but it is. It gives you a lot of power when debugging things or playing around with new things (something I haven't seen discussed re:modules on this list). One of the greatest things in JS is that instead of reading the usually poor documentation, let alone the code, of something you want to use you can just load it up in node or the developer tools and play around with it. With node, you require() something in the repl and you see immediately what it exports. (...edit...) This is simply not possible with ES6 modules without a massive boilerplate spaghetti with async stuff. You're right, but I don't think we need objects-as-modules to address this. But depending on how you load the modules (i.e. syntax or Loader API) the module might end up being an object anyway so it's just confusing if it sometimes is and sometimes isn't. We want a blocking load API for these situations: 1. Any REPL (strong) 2. Server-only programs which don't care about async loading and don't want the complications (weaker) In es6now, I provide a `loadModule` function for loading ES6 modules synchronously in the REPL. I think Node would want to provide a synchronous loading method as part of the so-called module meta object. The term module meta object and simple design don't go hand in hand. That API needs eyes, BTW. Thanks for the reference, I'll take a look at it after having a good night's sleep first. :) Given all this, how are we supposed to convince people to use this stuff? These concerns are not something that can be fixed later either, they're fundamental to the current design. I don't see any technical problem here. So let's look at the sociological argument: ES6 modules are different from Node/AMD modules, and seem to be at odds philosophically. Since Node/AMD modules already have established user bases, and important members of the Node community are critical, ES6 modules won't be successful at penetrating the market. Counter-argument: Take a look at this package: https://github.com/zenparsing/zen-sh . It's an experimental ES6 package which allows you to open up a shell and execute commands using tagged template strings. I use it at work to automate git tasks. It's awesome (but very rough). It's completely installable using NPM, today. I encourage anyone to try it out (you'll need to install es6now https://github.com/zenparsing/es6now first, though). It exports a single thing, but that thing is given a name. It is set up to work with both `require` and `import`. Now, the sociological argument says that because it's written as an ES6 module the community will reject this package. Does that sound plausible to you? No of course not, but why would anyone introduce more complexity to their project to do what you did? Experimental curiosity is probably your case, but the only other reason I can think of is sadism, deliberately fragmenting the platform. In that example,
Re: Re: ModuleImport
The arguments for and against supporting cyclic dependencies seem to be academic. I'm yet to see any evidence of their importance in practice nor proof they they are fundamental ... or not. Transitive cyclic dependencies. I'd say that's the case that was in the minds of the authors of the module system. In large codebases those can happen and a module system that does not handle them gracefully would be poor. Support for them is needed, and what CommonJS has is not good enough. They are acknowledged in the modules documentation for node http://nodejs.org/api/all.html#all_cycles This does not mean they are recommended, the same holds true for ES6 modules. It is an acceptance of the reality of complex and large codebases that sometimes cyclic dependencies can occur. It boils down to this. You can import a dependency in three ways import MyClass from 'MyClass'; import {MyClass} from 'MyClass'; module myClass from 'MyClass'; That's one too many ways for the simplest module system that fulfills all requirements. import MyClass from 'MyClass'; import {MyClass} from 'MyClass'; import * as myClass from 'MyClass'; Is not the fix. The confusion stemmed from the first production not the last. Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. B. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
On Wed, Jul 2, 2014 at 3:29 PM, Brian Di Palma off...@gmail.com wrote: The arguments for and against supporting cyclic dependencies seem to be academic. I'm yet to see any evidence of their importance in practice nor proof they they are fundamental ... or not. Support for them is needed, and what CommonJS has is not good enough. They are acknowledged in the modules documentation for node http://nodejs.org/api/all.html#all_cycles This does not mean they are recommended, the same holds true for ES6 modules. It is an acceptance of the reality of complex and large codebases that sometimes cyclic dependencies can occur. That's awesome--I didn't know that node had arrived at exactly the same solution as Firefox did. It even uses the same test file names. Node's module system has way more users, and Firefox's is twice as old. Here's the 7-year old test for it: http://hg.mozilla.org/mozilla-central/annotate/38ecfc3922b8/js/xpconnect/tests/unit/test_recursive_import.js I wrote that, but I didn't have any conceptual insight. All I did was go what does python do and made the Mozilla JS engine do that. I think that's the critical disconnect in this thread--we have the ability to change the JS implementations, so we're not fenced in by the way JavaScript currently works. The authors of node.js and Firefox arrived at that conclusion, anyway. - Rob ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
So I think we've gone over the interoperability argument already. That is, with default exports (or module-object-overwriting), you can write something like this: import x from some-old-module; Without default exports, you'll have to use something like one of the following: import { exports as x } from some-old-module; import { default as x } from some-old-module; import { _ as x } from some-old-module; The user experience is a little better with default exports, but we shouldn't be overly concerned with optimizing the loading old-style modules. The experience without default exports is good enough. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
There are three arguments for default exports: 1. Anonymous exports are a value-adding feature, as it allows the user to use a library without having to know the export name. 2. Default exports allow more streamlined renaming of the imported binding. 3. Default exports allow smoother interoperability with legacy modules. The responses to these arguments are: 1. It's not clear that anonymous APIs add value, since the user of an API always has to refer to it's documentation, and therefore will always be exposed to its exported API names. 2. From experience with other languages like Python, we know that import renaming is the exception rather than the rule. As such, streamlined renaming only results in a marginal benefit. 3. Interoperability with legacy modules is possible by using a simple naming convention. Default exports would improve the user experience of importing from legacy modules somewhat, but optimizing legacy modules should not be a primary motivating factor in the design. And 4. The default export feature is confusing to users, because it gives the appearance of module-object-overwriting (in the Node sense) while acting completely different on a semantic level. Syntactically, the design favors default exports while semantically it favors named exports. Since the module system is the entry point to the language for users of all experience levels, the module system should not be confusing. It should be as simple and obvious as possible. Given that the costs (4) do not outweigh the benefits (1, 2, 3), default exports should be dropped. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
My recommendation is to drop the default export feature and leave everything else as is (except perhaps for making module.x equivalent to x per Andreas). Given that the current module system has far better support for circular dependencies than module-as-module designs, the static export design should be retained. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
My recommendation is to drop the default export feature and leave everything else as is (except perhaps for making module.x equivalent to x per Andreas). Given that the current module system has far better support for circular dependencies than module-as-module designs, the static export design should be retained. er, module-as-object designs : ) ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
If it had good enough support for circular dependencies, would we be able to make sense of module-as-module designs? Side note: I meant object-as-module really. It's kinda funny that I capped off all of that with such a silly mistype. : ) ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
one benefit of default exports is forcing people to choose one of export { x as exports }; export { x as default }; export { x as _ }; this is the one chance to get everyone on the same page as far as object-as-module having circular dependency issues, can you elaborate on that, I understand how let {foo, bar} = import './baz'; would have circular reference problems (amongst other issues), but module name from './path' ... name.method(); is (~)what node uses and has comparable (better for certain things, worse for others) circular dependency support (source: writing a CJS loader using the es6 loader hooks api) the difficulties I can see with modules as objects involve static analysis. On Tue, Jul 1, 2014 at 1:22 PM, Kevin Smith zenpars...@gmail.com wrote: If it had good enough support for circular dependencies, would we be able to make sense of module-as-module designs? Side note: I meant object-as-module really. It's kinda funny that I capped off all of that with such a silly mistype. : ) ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- -Calvin W. Metcalf ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Tue, Jul 1, 2014 at 1:36 PM, Calvin Metcalf calvin.metc...@gmail.com wrote: one benefit of default exports is forcing people to choose one of export { x as exports }; export { x as default }; export { x as _ }; this is the one chance to get everyone on the same page I think if default exports were dropped, it would certainly be worth an explicit non-normative mention in the spec text that the export named _ is reserved for interoperability with legacy module systems or something like that. There aren't *that* many widely-used module systems, social pressure and an explicit recommendation is probably sufficient to get them all on the same page. (And presumably the legacy loader implementations which provide interoperability have a good deal of influence here.) as far as object-as-module having circular dependency issues, can you elaborate on that, I understand how [...] is (~)what node uses and has comparable (better for certain things, worse for others) circular dependency support (source: writing a CJS loader using the es6 loader hooks api) the difficulties I can see with modules as objects involve static analysis. This is, indeed, worth exploring further. Node.js has a very straightforward mechanism for dealing with circular dependencies. As long as you make all of your references indirectly through the module object they are mutable. This is pretty much exactly equivalent to what ES6 modules provide, with the exception that they save the required modulename. prefix -- but at the cost of possible user confusion, since JavaScript has never had mutable bindings in the way that the ES6 module system provides them. If we're cutting things from the ES6 module spec, can we consider cutting the magical mutable `import {foo} from ./foo;` bindings as well? Experience has shown that the rare cases where circular dependencies are intended and used can deal with the overhead of prefixing the module object. Personally, I would like to introduce mutable bindings as a future language-level feature, not as some weird wart on the module design. --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Can't proxies or getters/setters be used to get objects to behave like they have named exports? As in, when getting a property of the object it looks up the current value of the bound value in the module. I can't see why this wouldn't work, but there is probably a reason since nobody has proposed it yet. Marius Gundersen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
module name from './path' ... name.method(); Right - that would work for objects-as-modules, but this wouldn't: import { something } from ./path; Unless you somehow emitted getters for all references to something. Without consulting documentation, I would expect that these two things: module x from ./path; // From within some function or other x.foo(); import { foo } from ./path; // From within some function or other foo(); would be equivalent. I think a beginner would find a difference there surprising. Also, consider hoisting. // Call an imported function directly import { foo } from ./path; foo(123); module m from ./path; m.foo(); With object-as-module, neither one is going to work if you have a circular dependency at play. Of course, these gains aren't giant - they support edge cases. But they are real. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: ModuleImport
From: es-discuss es-discuss-boun...@mozilla.org on behalf of C. Scott Ananian ecmascr...@cscott.net If we're cutting things from the ES6 module spec, can we consider cutting the magical mutable `import {foo} from ./foo;` bindings as well? Experience has shown that the rare cases where circular dependencies are intended and used can deal with the overhead of prefixing the module object. Yes, please. Strong +1. As long as we're in the magical land of theorycraft where we're dropping features of consensus-modules, and completely off the topic of the OP: let's drop mutable bindings, and keep default exports. Personally, I would like to introduce mutable bindings as a future language-level feature, not as some weird wart on the module design. I would rather never introduce them at all. But I agree with the sentiment that having them sneak in through the module syntax backdoor is silly. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
If it had good enough support for circular dependencies, would we be able to make sense of module-as-module designs? OK, so let's assume for the sake of argument that objects-as-modules is not confusing, so (4) doesn't apply. All of the arguments for and against default exports also apply to module-object-overwriting. As such, we are balancing the marginal user experience gains of export-overwriting against the better support for circular dependencies of real modules. And we must note that allowing users to overwrite the module-object eliminates the advantages of statically analyzable exports in those cases. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
As such, we are balancing the marginal user experience gains of export-overwriting against the better support for circular dependencies of real modules. Another way of looking at it: Being in control of the language, you can always invent new sugar to optimize user experience (within limits) when real usage data proves its worth. But the core model can't be changed. As such, it seems like it would be better to err on the side of making the core model simple and solid, leaving minor UX optimizations to future syntax. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On 27 June 2014 21:45, C. Scott Ananian ecmascr...@cscott.net wrote: On Fri, Jun 27, 2014 at 3:34 PM, Andreas Rossberg rossb...@google.com wrote: All this means is that there will effectively be two different module systems, and in practice, every module provider has to pick one. Which is a problem, not a solution. ...this is why I've been arguing strongly for consistent syntax regardless. Static checking and lazy binding should be value added features, not something I have to think about every time I import a module. Painting over semantic differences by using the same syntax is not an improvement in usability. It decreases readability, and increases confusion and surprise. You probably weren't around when this was discussed last time, but the differences between importing from a proper module and importing from a singleton export object are not just in static checking. They create different bindings. The former creates an _alias_ for the variable from the module. The latter would need to destructure the property into a _new_ variable, i.e., would need to _copy_ its state. This leads to visibly different results when the variable is mutated (internally or externally). Please assume that all these ideas have been discussed extensively, and carefully considered, in particular by Dave and Sam. And while I do not agree with all conclusions, the current design has gotten much more thought in it than many in this thread seem to assume. In particular, none of the suggestions made in this thread so far are new. The hybrid, best of both worlds approach you seem to have in mind is technically impossible, unfortunately. I look forward to a technical proof then. You have not yet provided that. Well, I don't know how it could be done. I think the proof obligation is on those who claim it's possible. ;) jslint/jshint is already a (hacky) proof for the most common cases. It detects undeclared variables. All you need to do is write a loader which will prepopulate the jshint's 'predef' field appropriately. More complicated sorts of static checking fall to Turing completeness. Ie: ``` import foo from foo; var bat = (Math.random() 1) ? foo : { }; console.log(bat.baz); // is this a static error? ``` The reasonable thing is to accept incompleteness and provide static checking of the common cases: barewords and module name.identifier productions. Both of those tests are quite capable of skipping tests if module name is not a pure module. And that is exactly why real ES6 modules are more structured. They are carefully crafted such that checking of imports/exports is _always_ possible. And as I said earlier, any attempt to make them interchangeable with legacy-style modules would break this property. We can decide that this checking is not worth it and drop it. But we cannot have our cake and eat it too. /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Let me try to restate my request for clear information on the advantages of module bindings vs module object architectures. The import syntax already allows pre-execution tools to collect every module that will be accessed by a root dependent module (including some that may not be used). This advantage is clear and it can be weighed against the disadvantages, including more syntax restrictions to learn and the need for a separate dynamic API. On balance I think it's a win, because I understand the advantage. What's not clear is the advantage of module bindings form for modules. When the world of possibilities opens up, what specific things will I see there? On Sun, Jun 29, 2014 at 7:46 PM, Kevin Smith zenpars...@gmail.com wrote: Bruno and John's arguments are classic examples of the straw man fallacy. In my concrete examples I made no reference to static type systems (or any type systems at all, for that matter). I merely pointed out that by allowing the programmer to provide compile-time information in the form of exports and declarative forms, a world of possibilities opens up. Of course, static information can always be *inferred* from dynamic. That's basically how JS engines work, but raising that up to some ideal principle is foolish dogmatism. They accuse me of advocating decades-old technology, but it is purely dynamic JS that is decades old. Evolve or die is the way. The we don't need no stinkin' classes argument is counter-productive, counter-intuitive reactionary garbage, and quite frankly it bores me. : P ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Sun, Jun 29, 2014 at 1:00 PM, Domenic Denicola dome...@domenicdenicola.com wrote: On Jun 29, 2014, at 12:50, Rick Waldron waldron.r...@gmail.com wrote: Static analysis would be necessary if JavaScript ever wanted to make macros possible in modules. I don't have exact numbers nor have I done any formal surveys, but the general response to Sweet.js has been overwhelmingly positive. It would be a shame to close that door. My understanding is that door is already closed, since we were not able to eliminate the global scope contour from modules (which is necessary to get completely-static knowledge of all free identifiers). Part of the rationalization for this was that compile-time tools (like Sweet.js) are working out pretty well. See Back to Static Checking in http://esdiscuss.org/notes/2013-09-18 Yes, it appears I forgot about this. Thanks for the follow up. Rick ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Mon, Jun 30, 2014 at 7:14 AM, Andreas Rossberg rossb...@google.com wrote: ...this is why I've been arguing strongly for consistent syntax regardless. Static checking and lazy binding should be value added features, not something I have to think about every time I import a module. Painting over semantic differences by using the same syntax is not an improvement in usability. It decreases readability, and increases confusion and surprise. You probably weren't around when this was discussed last time, but the differences between importing from a proper module and importing from a singleton export object are not just in static checking. They create different bindings. The former creates an _alias_ for the variable from the module. The latter would need to destructure the property into a _new_ variable, i.e., would need to _copy_ its state. This leads to visibly different results when the variable is mutated (internally or externally). Yes, I am aware of that. I also claim that in most module code this difference is insignificant. That is, it continues to be best practice to *avoid* creating circular dependencies in modules. Further, self-contained modules will have no need to export lazy-bound references, even if they use them internally. I *have* been around for these discussions. And my conclusion is that the dogmatists are getting in the way. Just because there are *some* differences between modules and objects doesn't mean that *all* users must be confronted with them. Just because *some* modules may be defined in a way to prevents robust checking of modules doesn't mean that *all* modules must be prevented from doing so. Please assume that all these ideas have been discussed extensively, and carefully considered, in particular by Dave and Sam. And while I do not agree with all conclusions, the current design has gotten much more thought in it than many in this thread seem to assume. In particular, none of the suggestions made in this thread so far are new. I agree that the discussion has gone in many circles. But it is also clear that the consensus is fragile and the compromises not well-liked. I don't know of a better way to make continued progress other than (a) proposing tweaks to the existing compromise that might enhance its appeal, or (b) continuing to discuss the trade-offs made in an effort to solidify the consensus. We can decide that this checking is not worth it and drop it. But we cannot have our cake and eat it too. Concretely I'm proposing that we drop only the must and all phrases with respect to checking. I think we can come up with a design where checking is mostly and usually possible. This will put the decision in the hands of the code authors, who can either decide to (a) gradually transition their legacy code bases to allow more checking, or (b) decide that the checking is not worth it *for their particular code base*. --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On 30 June 2014 19:01, C. Scott Ananian ecmascr...@cscott.net wrote: On Mon, Jun 30, 2014 at 7:14 AM, Andreas Rossberg rossb...@google.com wrote: ...this is why I've been arguing strongly for consistent syntax regardless. Static checking and lazy binding should be value added features, not something I have to think about every time I import a module. Painting over semantic differences by using the same syntax is not an improvement in usability. It decreases readability, and increases confusion and surprise. You probably weren't around when this was discussed last time, but the differences between importing from a proper module and importing from a singleton export object are not just in static checking. They create different bindings. The former creates an _alias_ for the variable from the module. The latter would need to destructure the property into a _new_ variable, i.e., would need to _copy_ its state. This leads to visibly different results when the variable is mutated (internally or externally). Yes, I am aware of that. I also claim that in most module code this difference is insignificant. That is, it continues to be best practice to *avoid* creating circular dependencies in modules. Further, self-contained modules will have no need to export lazy-bound references, even if they use them internally. I don't understand what you mean by lazy-bound references. In any case, the problem exists independent of circular module dependencies. I *have* been around for these discussions. And my conclusion is that the dogmatists are getting in the way. Just because there are *some* differences between modules and objects doesn't mean that *all* users must be confronted with them. Just because *some* modules may be defined in a way to prevents robust checking of modules doesn't mean that *all* modules must be prevented from doing so. I don't follow this line of reasoning. The language should define clearly what a module is, what you can expect from it, and it should do so consistently. Making it fuzzy and unreliable will just bite everybody, especially since modules will define the boundaries between code developed by different parties. If that's dogmatic then call me that. /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
It's not just about interoperability. It's also about enabling the pattern that proved itself to work quite well - module as a function, module as a class, module as a function with named exports attached in one namespace. I suppose if that pattern is not explicitly supported it might be just fine since perhaps we'll discover better patterns. And if not, we'll be able to just always use 1 named export and do ``` import {fs} from fs; import {moment} from moment; import {$} from jquery; // etc. ``` In fact, doesn't being able to import things like above make es6 modules already interoperable with CJS? On Mon, Jun 30, 2014 at 8:00 PM, Kevin Smith zenpars...@gmail.com wrote: What's not clear is the advantage of module bindings form for modules. When the world of possibilities opens up, what specific things will I see there? I think I'm following you now. So let's pose the question like this: Let's posit that there are no default export or import forms, otherwise the current *syntax* is the same. Now, we have two semantic options that we can attach to that syntax: 1. Modules are just regular objects with getters. The import declarations just get properties from those objects at runtime. 2. Modules are a collection of named bindings. Those bindings are resolved at compile-time. Now, since the syntax is the same, our ability to do offline static analysis is the same. So what are the differences between these two semantics? (All of this has been gone over before, of course...) Advantages of 1: - None? Advantages of 2: - Good support for circular dependencies - Mutable bindings - Compile-time optimizations for module.member expressions The advantage of 1 only appears if we allow module object overwriting. Those advantages are: - Auto-renaming the import (questionable) - Smoother interoperability with CommonJS modules. On the other hand, module object overwriting disables our ability to do offline static analysis. So I think the only forceful argument if favor of option 1 (i.e. modules are regular objects with getters) is interoperability, and only if we allow export overwriting. The interoperability question is therefore of central importance. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Jun 30, 2014 3:59 PM, Karolis Narkevičius karoli...@gmail.com wrote: In fact, doesn't being able to import things like above make es6 modules already interoperable with CJS? Almost, but not quite, since the name of the module itself ($, fs, etc) is not included in a typical commonjs module. So it would really be: ``` import {_ as $} from jquery; // etc ``` And from here the jump to default import as a small syntactic improvement on this seemed clear. (At the time at least.) --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
It's not just about interoperability. It's also about enabling the pattern that proved itself to work quite well - module as a function, module as a class, module as a function with named exports attached in one namespace. But we have to ask why that pattern worked out, and my take is that the user experience with CJS modules (which exported just a single) was poor. ES6 modules don't have that problem. I suppose if that pattern is not explicitly supported it might be just fine since perhaps we'll discover better patterns. That's the idea. And if not, we'll be able to just always use 1 named export and do ``` import {fs} from fs; import {moment} from moment; import {$} from jquery; // etc. ``` Right. In fact, doesn't being able to import things like above make es6 modules already interoperable with CJS? (Looks like Scott got here first...) Mostly. If you have a Node module which does the overwrite thing then you might have to assign an arbitrary export name to import it: import { exports as Emitr } from emitr; (Where exports is arbitrarily chosen by the module environment.) But there are other issues which make the interoperability thing more complicated: modules have different syntax than scripts, and modules must be parsed in strict mode. So you need to know before you load the module, what kind of module it is (ES6 or CJS). What we need is a complete interoperability story. I have some ideas which I'll write up when I get a chance. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Another interoperability issue is circular dependencies, es6 modules have support for certain kinds that cjs doesn't (like function declarations) and cjs has support for 2 modules using each others exports (as long as they arn't single exports). If it wasn't for this you could load cjs modules with just normalize and translate hooks. At the moment you can likely get around the difference with an instantiate hook, but I haven't had time to check yet. On Jun 30, 2014 4:11 PM, Kevin Smith zenpars...@gmail.com wrote: It's not just about interoperability. It's also about enabling the pattern that proved itself to work quite well - module as a function, module as a class, module as a function with named exports attached in one namespace. But we have to ask why that pattern worked out, and my take is that the user experience with CJS modules (which exported just a single) was poor. ES6 modules don't have that problem. I suppose if that pattern is not explicitly supported it might be just fine since perhaps we'll discover better patterns. That's the idea. And if not, we'll be able to just always use 1 named export and do ``` import {fs} from fs; import {moment} from moment; import {$} from jquery; // etc. ``` Right. In fact, doesn't being able to import things like above make es6 modules already interoperable with CJS? (Looks like Scott got here first...) Mostly. If you have a Node module which does the overwrite thing then you might have to assign an arbitrary export name to import it: import { exports as Emitr } from emitr; (Where exports is arbitrarily chosen by the module environment.) But there are other issues which make the interoperability thing more complicated: modules have different syntax than scripts, and modules must be parsed in strict mode. So you need to know before you load the module, what kind of module it is (ES6 or CJS). What we need is a complete interoperability story. I have some ideas which I'll write up when I get a chance. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Mon, Jun 30, 2014 at 12:00 PM, Kevin Smith zenpars...@gmail.com wrote: What's not clear is the advantage of module bindings form for modules. When the world of possibilities opens up, what specific things will I see there? I think I'm following you now. So let's pose the question like this: Let's posit that there are no default export or import forms, otherwise the current *syntax* is the same. Now, we have two semantic options that we can attach to that syntax: 1. Modules are just regular objects with getters. The import declarations just get properties from those objects at runtime. 2. Modules are a collection of named bindings. Those bindings are resolved at compile-time. Now, since the syntax is the same, our ability to do offline static analysis is the same. So what are the differences between these two semantics? Thanks! This is the kind of information I was hoping for. (All of this has been gone over before, of course...) Perhaps, but clear explanation of complex topics is difficult. Isolating the issues and getting to specifics helps. In that spirit I'll ask for more details. Advantages of 1: - None? Advantages of 2: - Good support for circular dependencies - Mutable bindings As far as I know, this is not something JS developers understand. What is it and what makes it an advantage? - Compile-time optimizations for module.member expressions Is this known to be significant and fundamental? As in there is no way to achieve this optimization with the other format? Seems to me that the compiler introduces the getters and thus can inline them. The advantage of 1 only appears if we allow module object overwriting. Concretely you mean monkey patching module objects in the importing code? Or? Those advantages are: - Auto-renaming the import (questionable) - Smoother interoperability with CommonJS modules. On the other hand, module object overwriting disables our ability to do offline static analysis. So I think the only forceful argument if favor of option 1 (i.e. modules are regular objects with getters) is interoperability, and only if we allow export overwriting. The interoperability question is therefore of central importance. Based on other discussion on this point, it seems like module-as-named-bindings can interoperate with CJS and AMD except perhaps in some unusual cases. Clarifying these cases would help. To me, these two lists are pretty darn small compared with the overall advantages of modules. jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Mon, Jun 30, 2014 at 4:32 PM, John Barton johnjbar...@google.com wrote: Based on other discussion on this point, it seems like module-as-named-bindings can interoperate with CJS and AMD except perhaps in some unusual cases. Clarifying these cases would help. To me, these two lists are pretty darn small compared with the overall advantages of modules. Note that, although this particular thread has gotten hung up on default import/export, mutable bindings, and export, there are some other significant incompatibilities. Bundling is one such example. Integration with HTML fetch is another. I defer to @jrburke, @domenic, and @hixie for the details. --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
- Mutable bindings As far as I know, this is not something JS developers understand. What is it and what makes it an advantage? Since imported bindings are just aliases for variables over in the originating module, changing the value of that variable in the exporting module will change value of the corresponding binding on the import side. This is important for circular dependencies, because an imported binding needs to get updated when the initialization phase runs to completion. Outside of circular dependencies, I don't know. I'm sure someone can find a good use case for it : ) - Compile-time optimizations for module.member expressions Is this known to be significant and fundamental? As in there is no way to achieve this optimization with the other format? Seems to me that the compiler introduces the getters and thus can inline them. If there is no module object overwriting, then I suppose it could be inlined. If you allow overwriting, then you would need the further restriction that you cannot import from overridden module objects. The advantage of 1 only appears if we allow module object overwriting. Concretely you mean monkey patching module objects in the importing code? Or? I mean something equivalent to `module.exports = whatever`, where the object stored in the registry was replaced by some arbitrary, user-specified object. The interoperability question is therefore of central importance. Based on other discussion on this point, it seems like module-as-named-bindings can interoperate with CJS and AMD except perhaps in some unusual cases. Clarifying these cases would help. As Scott and I pointed out, unless you have module-object overwriting (or default exports), then you have to use some arbitrary name for CJS modules that use the `module.exports = x` idiom. And that's the only real advantage of the module-as-object semantics. In fact, it doesn't really fly without it. To me, these two lists are pretty darn small compared with the overall advantages of modules. So we should just pick a core semantics and get on with it? I agree, but we have to decide whether module-object-overwriting is a design requirement or not. To sum up, the strongest advantages are: Option 1A (modules are regular objects which can be overwritten): Possibly better interoperability with CJS modules Option 2 (modules as named remote bindings): Better support for circular dependencies ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
If there is no module object overwriting, then I suppose it could be inlined. If you allow overwriting, then you would need the further restriction that you cannot import from overridden module objects. Sorry, strike the second sentence. (Trying to do too many things at once!) ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
I think that a possible compromise that can still make the ES6 module system more compatible with both AMD and CommonJS modules is by this: If there are no exports from a module, named or not, make the export process implementation-defined. If an ES5 Node module uses module.exports, then Node could configure the exports to be importable through the ES6 syntax. Some people use named exports, while others have even exported a constructor via module.exports. In the browser environment, it could be something along the lines of using the added Window properties as named exports. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Thanks Kevin. I'm going to challenge your list below, but I hope you don't take it negatively. I want the case for the module system to be as strong as possible. On Sat, Jun 28, 2014 at 11:51 AM, Kevin Smith zenpars...@gmail.com wrote: Static checking of imports and exports has well-known advantages and would help the long-term viability of the language. Enumerating these specific advantages would inform this discussion. These advantages are not well-known. Many developers have experienced the disadvantages of complex systems of rules and thus favor simple solutions over ones with theoretical advantages. Explaining the benefits concretely would help them balance the well-known costs. So pretty much everything in Javascript is dynamic, which is one reason why IDE support for Javascript has always lagged behind. You simply can't know what anything is until you actually run the program. Statically verifiable exports gives us the ability to inspect and analyze code without having to run it. There are two big benefits that this affords us: ## Early Errors and Warnings ## Let's say that you want to deprecate and remove an exported member from a module within a large JS code base. With static imports, the system will generate an error at compile time if something on the other side of that codebase is importing it. It seems to me that the compiler can verify these two statements with equal success: import {foo} from './foo'; var foo = require('./foo.js').foo; I agree that as a practical matter compilers may be more likely to implement checks on the first form simply because it is standard. And I agree that the language-defined module syntax will lead to better quality tools simply because more developers have formal training in compiler technology than have training in dynamic analysis. These are important pragmatic issues. Or am I wrong and these are not equivalent? Or there are examples which show the issue more clearly? For exported function declarations that use default parameters to indicate optional parameters, we can generate build-time warnings when such an function is imported and called with too few arguments. A good argument for default parameters. For exported classes, we have even more static information at our hands. Without having to run the program, we know the number of arguments for the constructor and we know the list of methods for class instances. We can generate warnings when we see an instance using a misspelled method name, for instance. A good argument for standard rather than ad-hoc class class syntax. ## Computer Aided Coding ## The information used above to generate lint-like warnings can also be used to give the developer in-editor feedback. Reliable code-completion for imported function and class declarations becomes possible. Again, for exported classes we can also do code completion for instance methods. These advantages may not seem like a big deal now, but imagine writing JS in a large team five years from now. Do you want the power of static analysis at your team's fingertips, or do you want to be stuck with anything goes so anything can break CommonJS modules? Does that do it? These are arguments for statically computable import module names -- so the compiler can determine the imports without executing source code -- and for top-level or hoisted (static) import statements -- so every import in a module is a dependency of the module without runtime conditionals. I believe that both of these limitations would be acceptable to almost all developers interested in using modules. Unfortunately I think we need more specific and detailed examples to understand the advantages of the static form. Thanks, jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Sat, Jun 28, 2014 at 3:58 PM, Kevin Smith zenpars...@gmail.com wrote: Static checking will be limited anyway. If you want to go this way you should use typescript. That's the point that I'm trying to make, shops will choose other languages that provide more static information. We should be thinking about expanding the user base and ensuring that JS is a viable option years down the road. JavaScript's enormous user base is the strongest possible evidence that static analysis provides no advantage to programming language viability. Static analysis may encourage some new users; overall complexity may discourage as many. (I recently started using a typed version of JS; I am not impressed.) Any survey of the top languages in actual use clear demonstrates that the runtime platform and app goals dominate language choice. Even within a platform it is clear static checks are way down the list of valued features. Rather than point towards type-checking, I think we should focus on the actual checks offered by the module design. It seems that these would come with a small cost quite unlike type-checking. CommonJS falls a bit short on the import side because static analysis of require calls is brittle. A special language syntax would enable a robust static analysis of dependencies. If you don't have static exports, then how are you going to know if what you import is valid? You can't, without executing the program. If you don't execute the program, how do you know if the code you are checking is even called? Oh, you do plan to execute the program. Well there you go. (I just think it is so weird that JavaScript's huge advantage of rapid dynamic feedback for developers receives so little attention while so much is lavished on static technologies developed decades ago for a computing environment vastly inferior to our current world.) jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Jun 29, 2014, at 12:50, Rick Waldron waldron.r...@gmail.com wrote: Static analysis would be necessary if JavaScript ever wanted to make macros possible in modules. I don't have exact numbers nor have I done any formal surveys, but the general response to Sweet.js has been overwhelmingly positive. It would be a shame to close that door. My understanding is that door is already closed, since we were not able to eliminate the global scope contour from modules (which is necessary to get completely-static knowledge of all free identifiers). Part of the rationalization for this was that compile-time tools (like Sweet.js) are working out pretty well. See Back to Static Checking in http://esdiscuss.org/notes/2013-09-18 ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
CommonJS falls a bit short on the import side because static analysis of require calls is brittle. A special language syntax would enable a robust static analysis of dependencies. If you don't have static exports, then how are you going to know if what you import is valid? You can't, without executing the program. The main purpose of modules is not to provide static type checking but to prevent global scope pollution and allow loaders to load source code reliably and efficiently. What I meant by static analysis was the ability for a loader to bundle all the dependencies so that they can be transported efficiently. With require this analysis is brittle because require is not a reserved keyword and its argument can be any expression. It is important to focus the design on loader issues and keep things orthogonal. Why reinvent a special destructuring syntax when this is already addressed by existing language constructs? It can be handled by allowing any LHS after the as keyword: import module_path as lhs_expression; Why bother about about static type checking? If you want static typing, use Typescript, your modules will be exporting typed APIs; if you don't care use JavaScript. Sorry for the previous empty post. I hit the wrong key. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
Static checking will be limited anyway. If you want to go this way you shoud use typescript. If you don't want static checking you should stick with ES3. Fixed that for you. Yes big projects are possible with JS, I work on them everyday. It would be nice if the language made them easier, that's what we are talking about. Big projects are possible with C, why bother with any other language? With sufficient rigor Assembly will do the trick. Just have good code reviews and test. ability to dynamically import modules in addition to static imports. IMO this should be packaged as an async API I think this is already possible using the loader. hooks for transpilers. This should also be an API Ditto. Usually, I obtain the same benefit running the tests (and more, test that were the product of TDD workflow) I can forsee many people writing tests where they configure the module loader to load mocks instead of true dependencies. This could result in tests passing while there is a breakage in the application. If the language can make some of these problems go away then we are better off. It's just like tests, code review and linting, it's another type of verification. I think that a possible compromise that can still make the ES6 module system more compatible with both AMD and CommonJS modules The ES6 module system is compatible with CommonJS and AMD, I'm happily mixing the two together with libraries like SystemJS https://github.com/systemjs/systemjs/ For example https://github.com/briandipalma/flux-es6/blob/master/src/Store.js The Emitr class here is imported from a CommonJS module It is important to focus the design on loader issues and keep things orthogonal. The Loader is quite solid and well designed, I've not heard any major issues with it. What does it have to do with this discussion? import {foo} from './foo'; var foo = require('./foo.js').foo; I though those two statements weren't comparable? The import statement can only be present at the module top level while the require can be written in any code block. That leaves it open to the random number require issues which makes static checking impossible. As you pointed out it's not a language standard so I'd imagine that's another reason why tooling is so weak. Rather than point towards type-checking, I think we should focus on the actual checks offered by the module design. It seems that these would come with a small cost quite unlike type-checking. Unless I'm misunderstanding Kevin I think we've both talking about exactly that. I guess people saw static and automatically added type. Just static checking of import and export bindings. A much smaller scope feature then static type checking. This discussion has veered off track, it's about a new ModuleImport form, which grew into questioning if default imports/exports was the real problem. I'd be interested in knowing if it's possible that instead of changing the ModuleImport form the default import/export idea could be postponed instead. It could be added in later if there really was need for it. As far as I can tell libraries like SystemJS can smooth over issues caused by importing from legacy module systems like CommonJS and AMD. Leaving default imports/exports an odd third way to import that was added based on some notion of backward compatability that wasn't needed. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Bruno and John's arguments are classic examples of the straw man fallacy. In my concrete examples I made no reference to static type systems (or any type systems at all, for that matter). I merely pointed out that by allowing the programmer to provide compile-time information in the form of exports and declarative forms, a world of possibilities opens up. Of course, static information can always be *inferred* from dynamic. That's basically how JS engines work, but raising that up to some ideal principle is foolish dogmatism. They accuse me of advocating decades-old technology, but it is purely dynamic JS that is decades old. Evolve or die is the way. The we don't need no stinkin' classes argument is counter-productive, counter-intuitive reactionary garbage, and quite frankly it bores me. : P ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
I'd be interested in knowing if it's possible that instead of changing the ModuleImport form the default import/export idea could be postponed instead. It can. Just to be clear, that means that we'd have these two forms on the import side: module FS from fs; import { stat } from fs; which would be cool with me. I think, though, that the default thing has syntactically been raised up to an architectural level, and that's the core problem that Andreas has pointed out. Once you make the architectural decision to drop it, then you'll probably never want it. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
(I get the digest...) First, I will say that you all beat me to my (almost) exact suggestion on the syntax, @Russell and @Scott. Second, `module foo from 'foo'` is counterintuitive, confusing, non-obvious, and really needs trashed IMHO. Now, to supplement these ideas, I will summarize how I think it may work best: // import default export(s) import 'lo-dash' as _; import 'fs' as fs; // import specific named export // imports as $.extend import extend in $ from 'jquery'; // import multiple named exports // imports as _, with members .map and .each import map, each in _ from 'underscore'; // import members of an export // imports those members into the module namespace import map, each from 'underscore'; // import all named exports as properties of _ import * in _ from 'lo-dash'; // import all named exports into module namespace import * from 'lo-dash'; // import as different name in current module import reallyReallyLongFunctionName as foo from './foo'; // import as different property name // likely atypical, would show up as something most wouldn't think // of a while down the road import reallyReallyLongFunctionName as func in foo from './foo'; The keyword in is used to both note that they are properties in the module. It is not as to limit confusion: as is better to dictate names. The basic syntax grammar could be defined as follows (please pardon my inability to format...I'm typing this from a phone): ModuleName: module-path ModuleName: 'module-path' ImportStatement: One of: DefaultImportStatement NamedImportStatement DefaultImportStatement: import ModuleName as Identifier; NamedImportStatement: import NamedImports from ModuleName; NamedImports: One of: NamedIdentifiers NamedImport NamedImport: NamedIdentifiers in Identifier NamedIdentifiers: NamedIdentifier , NamedIdentifiers NamedIdenifiers: NamedIdentifier NamedIdentifier: One of: Identifier Identifier as Identifier This is my proposed syntax. It is somewhat Pythonic, but slightly more verbose because file names aren't themselves restricted like identifiers. Here's a couple more real world examples: import 'jquery' as $; $('#button').attr('onClick', () = alert('You clicked the button!')); // == // import readdir, stat from 'fs'; import 'path' as path; function walk(dir, cb) { readdir(path.resolve(dir), (error, files) = files.forEach(file = { let file = dir + path.sep + file; let fileStat = stat(file); if (fileStat fileStat.isDirectory()) { walk(file, cb); } else { cb(file); } })); } Here's the version more popular out of the other suggestions: import {$} from 'jquery'; $('#button').attr('onClick', () = alert('You clicked the button!')); // == // import {readdir, stat} from 'fs'; import path from 'path'; function walk(dir, cb) { readdir(path.resolve(dir), (error, files) = files.forEach(file = { let file = dir + path.sep + file; let fileStat = stat(file); if (fileStat fileStat.isDirectory()) { walk(file, cb); } else { cb(file); } })); Which looks better? Which is more obvious and intuitive? On another note, look at the bright side and compare these with the ES5 equivalents: define(['jquery'], function ($) { $('#button').attr('onClick', function () { alert('You clicked the button!'); }); }); // == // var fs = require('fs'); var path = require('path'); function walk(dir, cb) { fs.readdir(path.resolve(dir), function (error, files) { files.forEach(function (file) { var file = dir + path.sep + file; var stat = fs.stat(file); if (stat stat.isDirectory()) { walk(file, cb); } else { cb(file); } }); }); } I think we've made some relatively good progress so far. That last is just unnecessarily complicated in syntax. aside Is it me, or is ECMAScript starting to become Python crossed with a curly-brace version of Scheme? I'm starting to see a lot of new functionally oriented syntax and methods being added to ES6 and considered for ES7, while at the same time Pythonic OOP and reflection being considered simultaneously. The arrow functions are effectively lambdas that can be complete functions in their own right. I wonder how long it will take for people will find out the ease of currying and more availability of functional techniques in ES6. var foo = (a, b) = (c) = a * b + c; var adder = a = b = a + b; var addOne = adder(1); var two = addOne(1); /aside -- Forwarded message -- From: Russell Leggett russell.legg...@gmail.com To: Kevin Smith zenpars...@gmail.com Cc: es-discuss es-discuss@mozilla.org Date: Thu, 26 Jun 2014 10:50:20 -0400 Subject: Re: ModuleImport Now the author can choose to export more things later without making breaking changes to the module. The only downside
Re: ModuleImport
I fully admit that I'm probably hanging a little behind in terms of emails because I get them bundled into 4-5 each. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Why make things so complex and introduce so many syntax variations? The following should be sufficient: import underscore as _; // var _ = require('underscore'); And there should be an API (not a language construct) to import a module dynamically (and asynchronously). Something like: promise = importModule(path); Why introduce an export syntax? Why not just use this (or exports)? this.foo = ... // or exports.foo = ... this = // or exports = ..., similar to module.exports = ... // importer obtains value of this / exports at end of module file, // as if `return this` was added at the end of the file. This would give the same power / flexibility as CommonJS and a simple compatibility path. This would eliminate the default exports problem because it would allow a module to export a function. It is also easy to explain. There is an operational requirement to have a special syntax for import because this is what allows loaders to build a dependency graph and optimize dependency loading. But there is no operational requirement to have a special syntax for export. Static checking on exported members feels odd. Bruno ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
This would give the same power / flexibility as CommonJS and a simple compatibility path. This would eliminate the default exports problem because it would allow a module to export a function. It is also easy to explain. So this is basically the sugar over CommonJS modules solution. But why bother? require already does all of this and there already exist tools to generate dependency graphs from require. No syntax is required. Static checking on exported members feels odd. Static checking of imports and exports has well-known advantages and would help the long-term viability of the language. Segments that want static checking to be a part of their workflow might very well just leave JS for some other language that provides it. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Sat, Jun 28, 2014 at 9:03 AM, Kevin Smith zenpars...@gmail.com wrote: Static checking on exported members feels odd. Static checking of imports and exports has well-known advantages and would help the long-term viability of the language. Enumerating these specific advantages would inform this discussion. These advantages are not well-known. Many developers have experienced the disadvantages of complex systems of rules and thus favor simple solutions over ones with theoretical advantages. Explaining the benefits concretely would help them balance the well-known costs. jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
What if the Loader spec had some attention given to match AMD/CommonJS for some cases and leave the new syntax for the new module semantics. Really what we want is for non-es6 module systems to be able to hook into the loader registry in a way that makes sense for them and will also make sense for IMPORTING those modules into es6 modules. require/define work in browsers and in node TODAY, the conversation imo shouldn't be about giving those systems better syntax it should be about creating a single registry/loader that easily supports all paths. - Matthew Robb On Sat, Jun 28, 2014 at 9:48 AM, John Barton johnjbar...@google.com wrote: On Sat, Jun 28, 2014 at 9:03 AM, Kevin Smith zenpars...@gmail.com wrote: Static checking on exported members feels odd. Static checking of imports and exports has well-known advantages and would help the long-term viability of the language. Enumerating these specific advantages would inform this discussion. These advantages are not well-known. Many developers have experienced the disadvantages of complex systems of rules and thus favor simple solutions over ones with theoretical advantages. Explaining the benefits concretely would help them balance the well-known costs. jjb ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Static checking of imports and exports has well-known advantages and would help the long-term viability of the language. Enumerating these specific advantages would inform this discussion. These advantages are not well-known. Many developers have experienced the disadvantages of complex systems of rules and thus favor simple solutions over ones with theoretical advantages. Explaining the benefits concretely would help them balance the well-known costs. So pretty much everything in Javascript is dynamic, which is one reason why IDE support for Javascript has always lagged behind. You simply can't know what anything is until you actually run the program. Statically verifiable exports gives us the ability to inspect and analyze code without having to run it. There are two big benefits that this affords us: ## Early Errors and Warnings ## Let's say that you want to deprecate and remove an exported member from a module within a large JS code base. With static imports, the system will generate an error at compile time if something on the other side of that codebase is importing it. For exported function declarations that use default parameters to indicate optional parameters, we can generate build-time warnings when such an function is imported and called with too few arguments. For exported classes, we have even more static information at our hands. Without having to run the program, we know the number of arguments for the constructor and we know the list of methods for class instances. We can generate warnings when we see an instance using a misspelled method name, for instance. ## Computer Aided Coding ## The information used above to generate lint-like warnings can also be used to give the developer in-editor feedback. Reliable code-completion for imported function and class declarations becomes possible. Again, for exported classes we can also do code completion for instance methods. These advantages may not seem like a big deal now, but imagine writing JS in a large team five years from now. Do you want the power of static analysis at your team's fingertips, or do you want to be stuck with anything goes so anything can break CommonJS modules? Does that do it? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Static checking will be limited anyway. If you want to go this way you shoud use typescript. Big projects are perfectly manageable with CommonJS from my experience with a 20+ team. The trick is to enforce code reviews and unit tests. CommonJS falls a bit short on the import side because static analysis of require calls is brittle. A special language syntax would enable a robust static analysis of dependencies. But the export side of CommonJS is basically ok. JS APIs are dynamic and there is little value in introducing a shallow and leaky static API verification at the module interface. Two things should not be overlooked in the design: * ability to dynamically import modules in addition to static imports. IMO this should be packaged as an async API * hooks for transpilers. This should also be an API Bruno Le 28 juin 2014 à 14:51, Kevin Smith zenpars...@gmail.com a écrit : Static checking of imports and exports has well-known advantages and would help the long-term viability of the language. Enumerating these specific advantages would inform this discussion. These advantages are not well-known. Many developers have experienced the disadvantages of complex systems of rules and thus favor simple solutions over ones with theoretical advantages. Explaining the benefits concretely would help them balance the well-known costs. So pretty much everything in Javascript is dynamic, which is one reason why IDE support for Javascript has always lagged behind. You simply can't know what anything is until you actually run the program. Statically verifiable exports gives us the ability to inspect and analyze code without having to run it. There are two big benefits that this affords us: ## Early Errors and Warnings ## Let's say that you want to deprecate and remove an exported member from a module within a large JS code base. With static imports, the system will generate an error at compile time if something on the other side of that codebase is importing it. For exported function declarations that use default parameters to indicate optional parameters, we can generate build-time warnings when such an function is imported and called with too few arguments. For exported classes, we have even more static information at our hands. Without having to run the program, we know the number of arguments for the constructor and we know the list of methods for class instances. We can generate warnings when we see an instance using a misspelled method name, for instance. ## Computer Aided Coding ## The information used above to generate lint-like warnings can also be used to give the developer in-editor feedback. Reliable code-completion for imported function and class declarations becomes possible. Again, for exported classes we can also do code completion for instance methods. These advantages may not seem like a big deal now, but imagine writing JS in a large team five years from now. Do you want the power of static analysis at your team's fingertips, or do you want to be stuck with anything goes so anything can break CommonJS modules? Does that do it? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Maybe, I cannot see all the landscape, but a minor comment, in my limited English Related to: ## Early Errors and Warnings ## Usually, I obtain the same benefit running the tests (and more, test that were the product of TDD workflow). In this way, I'm sure not only of no removal of something I needed, but also the underlying behavior of imported modules are still the same. Relaying on static imports only warns me about the presence or not of some functions, but the app could be broken Angel Java Lopez @ajlopez On Sat, Jun 28, 2014 at 7:42 PM, Bruno Jouhier bjouh...@gmail.com wrote: Static checking will be limited anyway. If you want to go this way you shoud use typescript. Big projects are perfectly manageable with CommonJS from my experience with a 20+ team. The trick is to enforce code reviews and unit tests. CommonJS falls a bit short on the import side because static analysis of require calls is brittle. A special language syntax would enable a robust static analysis of dependencies. But the export side of CommonJS is basically ok. JS APIs are dynamic and there is little value in introducing a shallow and leaky static API verification at the module interface. Two things should not be overlooked in the design: * ability to dynamically import modules in addition to static imports. IMO this should be packaged as an async API * hooks for transpilers. This should also be an API Bruno Le 28 juin 2014 à 14:51, Kevin Smith zenpars...@gmail.com a écrit : Static checking of imports and exports has well-known advantages and would help the long-term viability of the language. Enumerating these specific advantages would inform this discussion. These advantages are not well-known. Many developers have experienced the disadvantages of complex systems of rules and thus favor simple solutions over ones with theoretical advantages. Explaining the benefits concretely would help them balance the well-known costs. So pretty much everything in Javascript is dynamic, which is one reason why IDE support for Javascript has always lagged behind. You simply can't know what anything is until you actually run the program. Statically verifiable exports gives us the ability to inspect and analyze code without having to run it. There are two big benefits that this affords us: ## Early Errors and Warnings ## Let's say that you want to deprecate and remove an exported member from a module within a large JS code base. With static imports, the system will generate an error at compile time if something on the other side of that codebase is importing it. For exported function declarations that use default parameters to indicate optional parameters, we can generate build-time warnings when such an function is imported and called with too few arguments. For exported classes, we have even more static information at our hands. Without having to run the program, we know the number of arguments for the constructor and we know the list of methods for class instances. We can generate warnings when we see an instance using a misspelled method name, for instance. ## Computer Aided Coding ## The information used above to generate lint-like warnings can also be used to give the developer in-editor feedback. Reliable code-completion for imported function and class declarations becomes possible. Again, for exported classes we can also do code completion for instance methods. These advantages may not seem like a big deal now, but imagine writing JS in a large team five years from now. Do you want the power of static analysis at your team's fingertips, or do you want to be stuck with anything goes so anything can break CommonJS modules? Does that do it? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Static checking will be limited anyway. If you want to go this way you should use typescript. That's the point that I'm trying to make, shops will choose other languages that provide more static information. We should be thinking about expanding the user base and ensuring that JS is a viable option years down the road. CommonJS falls a bit short on the import side because static analysis of require calls is brittle. A special language syntax would enable a robust static analysis of dependencies. If you don't have static exports, then how are you going to know if what you import is valid? You can't, without executing the program. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Usually, I obtain the same benefit running the tests (and more, test that were the product of TDD workflow). In this way, I'm sure not only of no removal of something I needed, but also the underlying behavior of imported modules are still the same. Relaying on static imports only warns me about the presence or not of some functions, but the app could be Of course, there's no substitute for unit tests with good coverage. But the question is, what kind of help does that language provide for static analysis? Currently, JS provides almost none. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Thu, Jun 26, 2014 at 4:50 PM, Russell Leggett russell.legg...@gmail.com wrote: //import a single named export import foo from bar; //import multiple named exports import foo, baz from bar; //alias an imported named export import foo as fooAlias from bar; //import the module import bar as bar; That would put a lot of Node modules exporting a single object/function at a disadvantage. Compare: ```js var mkdr = require('mkdirp'); ``` to: ```js import mkdirp as mkdr from mkdirp; ``` I like that the most common module usage can be done simpler. -- Michał Gołębiowski ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Fri, Jun 27, 2014 at 3:41 AM, Michał Gołębiowski m.go...@gmail.com wrote: On Thu, Jun 26, 2014 at 4:50 PM, Russell Leggett russell.legg...@gmail.com wrote: //import a single named export import foo from bar; //import multiple named exports import foo, baz from bar; //alias an imported named export import foo as fooAlias from bar; //import the module import bar as bar; That would put a lot of Node modules exporting a single object/function at a disadvantage. Compare: ```js var mkdr = require('mkdirp'); ``` to: ```js import mkdirp as mkdr from mkdirp; ``` I like that the most common module usage can be done simpler. No, that example would be: import mkdirp as mkdir; Its actually shorter than node. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Thu, Jun 19, 2014 at 4:04 PM, David Herman dher...@mozilla.com wrote: On Jun 19, 2014, at 3:31 AM, Michał Gołębiowski m.go...@gmail.com wrote: Compare these 3 forms of importing all the module lodash bindings to an object _: ```js var _ = require(lodash); // Node import * as _ from lodash; // Dave's syntax import lodash as _; ``` My feeling is that the clutter is small, and it's acceptable to have it be slightly less minimal than default export since that is the case we are favoring. This isn't so cluttered as to be downright *punishing* multi-export utility modules (it's literally only two characters longer than your Node code, albeit admittedly a little chattier), but it's good that default import is the winner. IMO it's not even that much a question of taken space but of potential confusion. People will think if they can do: ```js import * as _ from lodash; ``` they can do: ```js import * from lodash; ``` as well. Also, there are reports of thinking that the first form exports not only the _ container object but also all individual methods as in Python. I'd still prefer: ```js import lodash as _; ``` I think it's less confusing that other proposals and reads naturally; it also corresponds more to other module systems. But if that's out of the picture, sth like: ```js import module _ from lodash; ``` would be IMO less confusing that the proposed `* as _` form. -- Michał Gołębiowski ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Fri, Jun 27, 2014 at 9:44 AM, Russell Leggett russell.legg...@gmail.com wrote: No, that example would be: import mkdirp as mkdir; Its actually shorter than node. But in current proposal the module object cannot be a function from what I understand. So you'd have to create a named export and import it as I wrote. -- Michał Gołębiowski ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Some observations: * I think the 'import * as x' syntax for module imports is not an improvement, for at least two reasons: - It raises the expectation that you can actually write 'import * from' (as already noted in this thread). - It removes the syntactic marker for binding a module identifier. That is problematic because (a) module identifiers come with extra static checks on their uses (or so I thought, see below), and it is (b) future hostile to lexical module declarations, because with those, only module identifiers could be used in certain contexts (e.g., on the RHS of an import-from). Thinking forward, I think it would be highly preferable to consistently mark all bindings of module identifiers with a 'module' keyword. * I think the controversy is because the module design tries pleasing two incompatible community goals, but instead of achieving that, it has acquired all the characteristics of a committee design (although, ironically, modules are probably the least committee-designed part of ES6 :) ): - Some want modules in the conventional sense, as encapsulated namespaces with named exports, which you can import and access qualified or unqualified, and where imports are checked. - Some want modules in the specific pre-ES6 JavaScript style, where they can be any arbitrary JS value, and in the good old JavaScript tradition that checking doesn't matter. The current design (including the newest suggestions for imports) seems to please neither side, because it tries to be the former semantically, but wants to optimise for the latter syntactically. The main outcome is confusion. * This is probably the 3rd or even 4th time round this aspect of modules is discussed controversially. That is not a good sign. I _really_ think we need to make up our minds: - Either we think real modules are an improvement, and checking is important. Then the model and the syntax should be consistent about that. Moreover, checking needs to consistently apply, no matter how a module and its components are defined or accessed. - Or we come to the conclusion that supporting the legacy singleton export model as a primary use case is a mandatory matter. Then we should drop attempts of building something in/semi-compatible, and limit innovation to making export and import declarative, and designing a loader API. The current design falls between stools with respect to syntax, leading to the confusion people complain about. And it falls between stools with respect to import checking, making it dependent on superficially syntactic choices (like, whether you use default export or not, or whether you use unqualified import or not), and thereby unreliable in practice. Quite honestly, I rather have no import checking than checking that only applies in some syntactic cases -- because that gives a false sense of security, harms refactoring, and/or encourages overuse of unqualified imports, hampering readability. And if we are willing to cut partial checking, then there actually is much less compelling reason to have all the machinery around module objects, import as aliasing, etc. The system could probably be simplified quite a bit, and get much closer to what proponents of legacy JS modules are used to. Of course, I'd much rather go with real modules. But more importantly, the system should make a choice. Something in the middle is looking more and more like the least attractive alternative -- it seems like substantial extra complexity for too little (or even negative) benefit. As surprising as it may sound to some, I'm starting to warm up to the legacy option if it avoids the floor between the stools. :) /Andreas On 19 June 2014 10:15, David Herman dher...@mozilla.com wrote: Thanks to everyone for working through the issues around ModuleImport. I know it's been frustrating, but the discussions have really helped clarify the key constraints. ## Constraints First, let me restate the most important arguments. In favor of removal: * **The syntactic distinction between ModuleImport and default import is vague.** We've consistently seen confusion between the semantics of ModuleImport and default export. While certainly some of the confusion might be chalked up to misinformation and lack of documentation, we've seen people be confused by this even when correctly explained the semantics. The problem with the syntax is that the only visual distinction is the initial keyword (`module` vs `import`), and it's not clear from that keyword which concept you're talking about. (Once you've internalized the structure of module instance objects, you get a hint from the `module` keyword, but that doesn't help people who are just learning the system, particularly if they're already familiar with other JS module systems.) Against removal: * **Without ModuleImport, authors of multi-export modules would be pressured to circumvent the named exports functionality.** Without ModuleImport, clients
Re: ModuleImport
- Either we think real modules are an improvement, and checking is important. Then the model and the syntax should be consistent about that. Moreover, checking needs to consistently apply, no matter how a module and its components are defined or accessed. - Or we come to the conclusion that supporting the legacy singleton export model as a primary use case is a mandatory matter. Then we should drop attempts of building something in/semi-compatible, and limit innovation to making export and import declarative, and designing a loader API. Since real modules are at the core of the current design, I think the first option is feasible and equates to simply dropping the default feature, leaving everything else intact. The second option, on the other hand, will take us back to the drawing board and I don't see how we can do that within the ES6 timeline. We don't want Promises all over again. So, if we cannot settle on option 1, then I think modules must be deferred from ES6. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
My opinion is that CommonJS and AMD work today and will continue to work into the future so we should optimize for the ideal looking forward, not backward case when adding to the language. Loader will still be overload-able and since both CommonJS and AMD require a library today it seems completely reasonable that they will continue to do that and can hook into es6 through loader extension. Conclusions: - Drop default exports - export function readFile(){} - import fs from es6-fs; // fs.readfile(); - import { readFile } from es6-fs; // readFile(); - Done. - Matthew Robb On Fri, Jun 27, 2014 at 6:32 AM, Kevin Smith zenpars...@gmail.com wrote: - Either we think real modules are an improvement, and checking is important. Then the model and the syntax should be consistent about that. Moreover, checking needs to consistently apply, no matter how a module and its components are defined or accessed. - Or we come to the conclusion that supporting the legacy singleton export model as a primary use case is a mandatory matter. Then we should drop attempts of building something in/semi-compatible, and limit innovation to making export and import declarative, and designing a loader API. Since real modules are at the core of the current design, I think the first option is feasible and equates to simply dropping the default feature, leaving everything else intact. The second option, on the other hand, will take us back to the drawing board and I don't see how we can do that within the ES6 timeline. We don't want Promises all over again. So, if we cannot settle on option 1, then I think modules must be deferred from ES6. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: ModuleImport
From: es-discuss [mailto:es-discuss-boun...@mozilla.org] On Behalf Of Kevin Smith The second option, on the other hand, will take us back to the drawing board and I don't see how we can do that within the ES6 timeline. We should not be concerned about timeframe in terms of what official Ecma spec revision modules land in. ES has moved to a train model, recognizing that it's more important when features ship in browsers than when Ecma publishes a copyrighted document containing the features. Modules are probably in stage 2, verging on stage 3, of [the TC39 process][1]. They won't make it into ES6 until they land in stage 4, and no implementation has started on them. It's not clear to me that keeping the current design would actually incentivize implementers to implement them any faster than if they dropped back to stage 1, especially since implementers generally prioritize features with high user demand, whereas modules in their current state are definitely mixed signals. [1]: https://docs.google.com/document/d/1QbEE0BsO4lvl7NFTn5WXWeiEIBfaVUF7Dk0hpPpPDzU/edit ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: ModuleImport
From: es-discuss [mailto:es-discuss-boun...@mozilla.org] On Behalf Of Matthew Robb My opinion is that CommonJS and AMD work today and will continue to work into the future so we should optimize for the ideal looking forward, not backward case when adding to the language. While this is a compelling argument in general, I am not sure it applies to modules, whose usefulness is based almost entirely on network effects. Indeed, the future we may be looking forward to is just one in which ES6 modules remain a [third competing standard][1] among the modules in the wild, and if they're sufficiently bad at satisfying existing use cases (as the rest of your message argues for), they will not compete very well. [1]: https://xkcd.com/927/ ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
We should not be concerned about timeframe in terms of what official Ecma spec revision modules land in. ES has moved to a train model, recognizing that it's more important when features ship in browsers than when Ecma publishes a copyrighted document containing the features. Fair enough - but the point still remains: the second option pushes us backward. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
+1 On Fri, Jun 27, 2014 at 6:39 AM, Matthew Robb matthewwr...@gmail.com wrote: My opinion is that CommonJS and AMD work today and will continue to work into the future so we should optimize for the ideal looking forward, not backward case when adding to the language. Loader will still be overload-able and since both CommonJS and AMD require a library today it seems completely reasonable that they will continue to do that and can hook into es6 through loader extension. Conclusions: - Drop default exports - export function readFile(){} - import fs from es6-fs; // fs.readfile(); - import { readFile } from es6-fs; // readFile(); - Done. - Matthew Robb On Fri, Jun 27, 2014 at 6:32 AM, Kevin Smith zenpars...@gmail.com wrote: - Either we think real modules are an improvement, and checking is important. Then the model and the syntax should be consistent about that. Moreover, checking needs to consistently apply, no matter how a module and its components are defined or accessed. - Or we come to the conclusion that supporting the legacy singleton export model as a primary use case is a mandatory matter. Then we should drop attempts of building something in/semi-compatible, and limit innovation to making export and import declarative, and designing a loader API. Since real modules are at the core of the current design, I think the first option is feasible and equates to simply dropping the default feature, leaving everything else intact. The second option, on the other hand, will take us back to the drawing board and I don't see how we can do that within the ES6 timeline. We don't want Promises all over again. So, if we cannot settle on option 1, then I think modules must be deferred from ES6. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
+1 Yeah - obviously +1 from me as well. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Fri, Jun 27, 2014 at 3:52 AM, Michał Gołębiowski m.go...@gmail.com wrote: On Fri, Jun 27, 2014 at 9:44 AM, Russell Leggett russell.legg...@gmail.com wrote: No, that example would be: import mkdirp as mkdir; Its actually shorter than node. But in current proposal the module object cannot be a function from what I understand. So you'd have to create a named export and import it as I wrote. Ok, so can I just ask a serious question. I looked at the mkdirp library, and in their own documentation, they use: var mkdirp = require('mkdirp'); So let's say in the new world order, no default exports, this is a named export mkdirp. Is it *really* that bad a thing to just use the name they chose for their API? I mean what about all of the likely cases of exporting a class. Would you advocate for those classes to be aliased before use? What about methods? Those are typically unchanged. The argument has been made that without default exports, library users would then have to known the name of the exported member. Is that really a serious complaint? You *have* to know what everything else is called, what parameters are expected, etc. I would go as far as to say that either 1 of 2 things is happening. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
var mkdirp = require('mkdirp'); Exactly. In ES, you would see this: import { mkdrip } from mkdirp; Python has a somewhat similar module system, and import-renaming is the exception, not the rule. Also, I've never heard anyone complain about having to know the name of a module member. In fact, specifying the exported name is essential for tracing out a code base that you're unfamiliar with. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Ok, so can I just ask a serious question. I looked at the mkdirp library, and in their own documentation, they use: var mkdirp = require('mkdirp'); So let's say in the new world order, no default exports, this is a named export mkdirp. Is it *really* that bad a thing to just use the name they chose for their API? I mean what about all of the likely cases of exporting a class. Would you advocate for those classes to be aliased before use? What about methods? Those are typically unchanged. The argument has been made that without default exports, library users would then have to known the name of the exported member. Is that really a serious complaint? You *have* to know what everything else is called, what parameters are expected, etc. I would go as far as to say that either 1 of 2 things is happening. Ok, accidentally sent... either 1 of 2 things is happening 1) The imported function is always called the same thing when imported. Its a de facto standard. 2) There is no de facto standard, and people call it a variety of things. I would argue that 1 is easy to transition, just export with that name, and 2 is not really a good thing. Its much harder to read or grep your code. It is an anti-pattern that should not be given so much sugar. Aliasing is still possible, but more awkward. I know Kevin and I both said this, but IMO the reason this pattern became so popular was to avoid: var mkdirp = require(mkdirp).mkdirp; In my proposal (no curlies), that's simply: import mkdirp from mkdirp; The big thing missing is just ease of aliasing, and I don't really see that as a problem. I'd love to hear an argument for why I'm wrong. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Fri, Jun 27, 2014 at 10:11 AM, Kevin Smith zenpars...@gmail.com wrote: var mkdirp = require('mkdirp'); Exactly. In ES, you would see this: import { mkdrip } from mkdirp; Python has a somewhat similar module system, and import-renaming is the exception, not the rule. Also, I've never heard anyone complain about having to know the name of a module member. In fact, specifying the exported name is essential for tracing out a code base that you're unfamiliar with. Haha, yes, you and I seem to be sharing a brain on this, I sent early and you wrote most of what I wrote. I still like curly-free, but that's bikeshedding to me. The important point is removing default exports. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
I wrote up a gist summarizing the different proposals that were being tossed around, it mainly says the same things as Andreas, just not as well https://gist.github.com/calvinmetcalf/5d9a88abaa9fe094e960\ On Fri, Jun 27, 2014 at 10:02 AM, Kevin Smith zenpars...@gmail.com wrote: +1 Yeah - obviously +1 from me as well. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss -- -Calvin W. Metcalf ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
RE: ModuleImport
This might be a good time to bring up this old thing: https://gist.github.com/domenic/1ab3f0daa7b37859ce43 At the time Yehuda and I put it together, we were much younger and more naive, but upon cursory review it seems not-horrible. It gives up mutable bindings but retains statically-verifiable multi-exports, along with a unified import syntax that doesn't require knowledge of how the module creator exported their objects, and exposes no refactoring hazards for transitioning between styles. I just realize it is probably not very compatible with computed property names though :( From: Andreas Rossbergmailto:rossb...@google.com Sent: 2014-06-27 09:07 To: David Hermanmailto:dher...@mozilla.com Cc: es-discuss@mozilla.orgmailto:es-discuss@mozilla.org Subject: Re: ModuleImport Some observations: * I think the 'import * as x' syntax for module imports is not an improvement, for at least two reasons: - It raises the expectation that you can actually write 'import * from' (as already noted in this thread). - It removes the syntactic marker for binding a module identifier. That is problematic because (a) module identifiers come with extra static checks on their uses (or so I thought, see below), and it is (b) future hostile to lexical module declarations, because with those, only module identifiers could be used in certain contexts (e.g., on the RHS of an import-from). Thinking forward, I think it would be highly preferable to consistently mark all bindings of module identifiers with a 'module' keyword. * I think the controversy is because the module design tries pleasing two incompatible community goals, but instead of achieving that, it has acquired all the characteristics of a committee design (although, ironically, modules are probably the least committee-designed part of ES6 :) ): - Some want modules in the conventional sense, as encapsulated namespaces with named exports, which you can import and access qualified or unqualified, and where imports are checked. - Some want modules in the specific pre-ES6 JavaScript style, where they can be any arbitrary JS value, and in the good old JavaScript tradition that checking doesn't matter. The current design (including the newest suggestions for imports) seems to please neither side, because it tries to be the former semantically, but wants to optimise for the latter syntactically. The main outcome is confusion. * This is probably the 3rd or even 4th time round this aspect of modules is discussed controversially. That is not a good sign. I _really_ think we need to make up our minds: - Either we think real modules are an improvement, and checking is important. Then the model and the syntax should be consistent about that. Moreover, checking needs to consistently apply, no matter how a module and its components are defined or accessed. - Or we come to the conclusion that supporting the legacy singleton export model as a primary use case is a mandatory matter. Then we should drop attempts of building something in/semi-compatible, and limit innovation to making export and import declarative, and designing a loader API. The current design falls between stools with respect to syntax, leading to the confusion people complain about. And it falls between stools with respect to import checking, making it dependent on superficially syntactic choices (like, whether you use default export or not, or whether you use unqualified import or not), and thereby unreliable in practice. Quite honestly, I rather have no import checking than checking that only applies in some syntactic cases -- because that gives a false sense of security, harms refactoring, and/or encourages overuse of unqualified imports, hampering readability. And if we are willing to cut partial checking, then there actually is much less compelling reason to have all the machinery around module objects, import as aliasing, etc. The system could probably be simplified quite a bit, and get much closer to what proponents of legacy JS modules are used to. Of course, I'd much rather go with real modules. But more importantly, the system should make a choice. Something in the middle is looking more and more like the least attractive alternative -- it seems like substantial extra complexity for too little (or even negative) benefit. As surprising as it may sound to some, I'm starting to warm up to the legacy option if it avoids the floor between the stools. :) /Andreas On 19 June 2014 10:15, David Herman dher...@mozilla.com wrote: Thanks to everyone for working through the issues around ModuleImport. I know it's been frustrating, but the discussions have really helped clarify the key constraints. ## Constraints First, let me restate the most important arguments. In favor of removal: * **The syntactic distinction between ModuleImport and default import is vague.** We've consistently seen confusion between the semantics of ModuleImport
Re: ModuleImport
My opinion is that CommonJS and AMD work today and will continue to work into the future so we should optimize for the ideal looking forward, not backward case when adding to the language. I think this statement points the way to something that we haven't yet discussed. A general question that we can apply to any language enhancement is: does this change merely satisfy existing users, or does this change carry the potential to expand the user base? I would say that Node-module-sugar would merely satisfy existing users, but real modules have the capacity to expand the JS user base to into segments that want static checking to be a part of their workflow. So to me the path forward is clear: we keep real modules, axe the default feature, and take a temporary hit of dissatisfaction from existing users so that we can expand the JS user base. The overriding concern should be the long-term viability of JS. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Fri, Jun 27, 2014 at 9:07 AM, Andreas Rossberg rossb...@google.com wrote: - It removes the syntactic marker for binding a module identifier. That is problematic because (a) module identifiers come with extra static checks on their uses (or so I thought, see below), and it is (b) future hostile to lexical module declarations, because with those, only module identifiers could be used in certain contexts (e.g., on the RHS of an import-from). Thinking forward, I think it would be highly preferable to consistently mark all bindings of module identifiers with a 'module' keyword. I disagree here. I think certain people are too hung up on what is a real module and what is an object pretending to be a module. For lots of legacy code (think jquery), we have objects like `$` which are actually both functions *and* modules (in the form of namespaces). The JS community can handle this. Honestly, most of the fun features of real modules are obscure corner cases anyway (like lazy binding). We can deal with a few oddball modules which are not actually module objects. - Some want modules in the conventional sense, as encapsulated namespaces with named exports, which you can import and access qualified or unqualified, and where imports are checked. - Some want modules in the specific pre-ES6 JavaScript style, where they can be any arbitrary JS value, and in the good old JavaScript tradition that checking doesn't matter. The current design (including the newest suggestions for imports) seems to please neither side, because it tries to be the former semantically, but wants to optimise for the latter syntactically. The main outcome is confusion. I think the problem is somewhat different, in that certain people resist the idea that some modules might not be actual modules. I think we could embrace that and the result would be *less* confusion (since we wouldn't require a syntactic marker for real module). It will be rare for someone to notice that `$.foo` is not lazily-bound, or that `$.module.path` (or whatever) isn't actually defined. If they care, they can file a bug against jquery. jquery should be able to either emulate the module functionality (duck typing! but this is one of the things that annoys me about the current way lazy binding works) or decide that backwards-compatibility or whatever is more important and ignore the issue. - Either we think real modules are an improvement, and checking is important. Then the model and the syntax should be consistent about that. Moreover, checking needs to consistently apply, no matter how a module and its components are defined or accessed. - Or we come to the conclusion that supporting the legacy singleton export model as a primary use case is a mandatory matter. Then we should drop attempts of building something in/semi-compatible, and limit innovation to making export and import declarative, and designing a loader API. Again, I think it's the either/or mindset which is the problem here. We can actually have both. Sometimes we won't be able to do strict checking. Sometimes we will. We don't need to force everyone into the same mold. Let the author of the module decide how important static checking, lazy binding, ease-of-use, etc, is and define their module appropriately. (And the same for the importer of the module.) Quite honestly, I rather have no import checking than checking that only applies in some syntactic cases -- because that gives a false sense of security, harms refactoring, and/or encourages overuse of unqualified imports, hampering readability. Again: is this what's hampering compromise? Can't we let the module author (and/or importer) decide the extent to which they think checking is important? Yes, modules will be a compromise design. But it's a compromise design because of *network effects* and *programmer preference*. There are large and useful codebases organized around different principles. We can't force them all into the same mold. Programming is still an art form and a matter of taste; let's go easy on the mandates. --scott (who actually thinks that current modules spec is not that bad, although it could be slightly improved) ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
I'm echoing Kevin here. I'd hope ES6 modules would offer me, a programmer working on large complex JS web apps, something more then what CommonJS/AMD offers. I work with codebases of 1K+ JS classes, 200K+ LOC and with many dependecies that are updated frequently. When our teams update depedencies the more static fast failures we can get compared to silent bugs the better. I will eat my hat if in 10 years people will be doing greenfield development using CommonJS/AMD instead of ES6 modules. Provide the language users with something *better* then what we have, don't just match it. I like CommonJS, npm, node as they offer great solutions given their constraints. This is the language level though, the expressive power and constraints are different. Knowing the name of a module export is not an issue, it's not an issue for people using global scripts or any other module systems/languages. I'd prefer a real module system. Especially considering that classes are being added and when you are building front end applications with a lot of state classes fit well. I can see many applications being mainly composed of modules with a single class exported and the most logical export name would be the classname. I've tried to use default exports for these situations and they don't seem to offer much value at all. import {MyClass} from './myclass'; The value of default exports seems low here. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On 27 June 2014 18:36, C. Scott Ananian ecmascr...@cscott.net wrote: On Fri, Jun 27, 2014 at 9:07 AM, Andreas Rossberg rossb...@google.com wrote: - It removes the syntactic marker for binding a module identifier. That is problematic because (a) module identifiers come with extra static checks on their uses (or so I thought, see below), and it is (b) future hostile to lexical module declarations, because with those, only module identifiers could be used in certain contexts (e.g., on the RHS of an import-from). Thinking forward, I think it would be highly preferable to consistently mark all bindings of module identifiers with a 'module' keyword. I disagree here. I think certain people are too hung up on what is a real module and what is an object pretending to be a module. For lots of legacy code (think jquery), we have objects like `$` which are actually both functions *and* modules (in the form of namespaces). The JS community can handle this. Honestly, most of the fun features of real modules are obscure corner cases anyway (like lazy binding). We can deal with a few oddball modules which are not actually module objects. I think you are missing the central problem. If imports/exports are to be statically checked, then module bindings, their exports, and their uses have to be statically recognisable and analysable. That prohibits intermixing proper modules with other random objects. Furthermore, it also requires module identifiers to be distinguishable by context. (And my point above was that, since this matters for the semantics, the programmer is best served when she can immediately distinguish binding forms accordingly.) - Some want modules in the conventional sense, as encapsulated namespaces with named exports, which you can import and access qualified or unqualified, and where imports are checked. - Some want modules in the specific pre-ES6 JavaScript style, where they can be any arbitrary JS value, and in the good old JavaScript tradition that checking doesn't matter. The current design (including the newest suggestions for imports) seems to please neither side, because it tries to be the former semantically, but wants to optimise for the latter syntactically. The main outcome is confusion. I think the problem is somewhat different, in that certain people resist the idea that some modules might not be actual modules. I think we could embrace that and the result would be *less* confusion (since we wouldn't require a syntactic marker for real module). It will be rare for someone to notice that `$.foo` is not lazily-bound, or that `$.module.path` (or whatever) isn't actually defined. If they care, they can file a bug against jquery. jquery should be able to either emulate the module functionality (duck typing! but this is one of the things that annoys me about the current way lazy binding works) or decide that backwards-compatibility or whatever is more important and ignore the issue. You argue that throwing out checking is fine, i.e., you are arguing for the second goal, and against the first. But others disagree. - Either we think real modules are an improvement, and checking is important. Then the model and the syntax should be consistent about that. Moreover, checking needs to consistently apply, no matter how a module and its components are defined or accessed. - Or we come to the conclusion that supporting the legacy singleton export model as a primary use case is a mandatory matter. Then we should drop attempts of building something in/semi-compatible, and limit innovation to making export and import declarative, and designing a loader API. Again, I think it's the either/or mindset which is the problem here. We can actually have both. Sometimes we won't be able to do strict checking. Sometimes we will. We don't need to force everyone into the same mold. Let the author of the module decide how important static checking, lazy binding, ease-of-use, etc, is and define their module appropriately. (And the same for the importer of the module.) Quite honestly, I rather have no import checking than checking that only applies in some syntactic cases -- because that gives a false sense of security, harms refactoring, and/or encourages overuse of unqualified imports, hampering readability. Again: is this what's hampering compromise? Can't we let the module author (and/or importer) decide the extent to which they think checking is important? No, the importers have no choice, they have to go with whatever the module provider picked. You cannot turn on checking on the use site alone, because it will lack the necessary static information from the definition site. Only real modules can provide that information, because only their definitions are sufficiently structured. That is fundamentally so. The hybrid, best of both worlds approach you seem to have in mind is technically impossible, unfortunately. /Andreas
Re: ModuleImport
On Fri, Jun 27, 2014 at 3:17 PM, Andreas Rossberg rossb...@google.com wrote: I think you are missing the central problem. If imports/exports are to be statically checked, then module bindings, their exports, and their uses have to be statically recognisable and analysable. That prohibits intermixing proper modules with other random objects. No, it means that your checker will only work with 'proper' modules. The linter can easily have a white/blacklist to handle oddball cases. You argue that throwing out checking is fine, i.e., you are arguing for the second goal, and against the first. But others disagree. No. I believe that *allowing the user (or module author) to selectively ignore* the checking (for example, by using a default import of an object which is not a module). You seem to be missing my fundamental point: this is not a dichotomy. You can check many imports, even if you can't check all. If the module author chooses to export a function instead of an object, you can't check the imports of *that one module*. Use a different module if that bothers you! No, the importers have no choice, they have to go with whatever the module provider picked. The importers have the choice to use a different module (or fork the module, or use a transpiler, or...). The module author has the choice to re/structure his module to allow lazy binding/checking *iff the users demand*. The hybrid, best of both worlds approach you seem to have in mind is technically impossible, unfortunately. I look forward to a technical proof then. You have not yet provided that. --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On 27 June 2014 17:32, Kevin Smith zenpars...@gmail.com wrote: So to me the path forward is clear: we keep real modules, axe the default feature, and take a temporary hit of dissatisfaction from existing users so that we can expand the JS user base. Note that the other half of my argument was that real modules are only worth the complexity when they provide checking _consistently_. That is, it shouldn't matter whether I write import {f, g, h} from url f(); g(); h() or module M from url M.f(); M.g(); M.h() These should be freely interchangeable -- the programmer shouldn't need to pick between getting import checking but polluting the scope and making uses less readable, or the other way round. The current semantics falls short on that, it doesn't check in the latter case. So from my perspective, that would need to be fixed as well. Which would be fairly easy. /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
import {f, g, h} from url f(); g(); h() or module M from url M.f(); M.g(); M.h() These should be freely interchangeable -- the programmer shouldn't need to pick between getting import checking but polluting the scope and making uses less readable, or the other way round. The current semantics falls short on that, it doesn't check in the latter case. I didn't even realize that was the case. So from my perspective, that would need to be fixed as well. Which would be fairly easy. I totally agree. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On 27 June 2014 21:26, C. Scott Ananian ecmascr...@cscott.net wrote: On Fri, Jun 27, 2014 at 3:17 PM, Andreas Rossberg rossb...@google.com wrote: I think you are missing the central problem. If imports/exports are to be statically checked, then module bindings, their exports, and their uses have to be statically recognisable and analysable. That prohibits intermixing proper modules with other random objects. No, it means that your checker will only work with 'proper' modules. The linter can easily have a white/blacklist to handle oddball cases. You argue that throwing out checking is fine, i.e., you are arguing for the second goal, and against the first. But others disagree. No. I believe that *allowing the user (or module author) to selectively ignore* the checking (for example, by using a default import of an object which is not a module). You seem to be missing my fundamental point: this is not a dichotomy. You can check many imports, even if you can't check all. If the module author chooses to export a function instead of an object, you can't check the imports of *that one module*. Use a different module if that bothers you! No, the importers have no choice, they have to go with whatever the module provider picked. The importers have the choice to use a different module (or fork the module, or use a transpiler, or...). The module author has the choice to re/structure his module to allow lazy binding/checking *iff the users demand*. All this means is that there will effectively be two different module systems, and in practice, every module provider has to pick one. Which is a problem, not a solution. The hybrid, best of both worlds approach you seem to have in mind is technically impossible, unfortunately. I look forward to a technical proof then. You have not yet provided that. Well, I don't know how it could be done. I think the proof obligation is on those who claim it's possible. ;) /Andreas ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Fri, Jun 27, 2014 at 3:34 PM, Andreas Rossberg rossb...@google.com wrote: All this means is that there will effectively be two different module systems, and in practice, every module provider has to pick one. Which is a problem, not a solution. ...this is why I've been arguing strongly for consistent syntax regardless. Static checking and lazy binding should be value added features, not something I have to think about every time I import a module. The hybrid, best of both worlds approach you seem to have in mind is technically impossible, unfortunately. I look forward to a technical proof then. You have not yet provided that. Well, I don't know how it could be done. I think the proof obligation is on those who claim it's possible. ;) jslint/jshint is already a (hacky) proof for the most common cases. It detects undeclared variables. All you need to do is write a loader which will prepopulate the jshint's 'predef' field appropriately. More complicated sorts of static checking fall to Turing completeness. Ie: ``` import foo from foo; var bat = (Math.random() 1) ? foo : { }; console.log(bat.baz); // is this a static error? ``` The reasonable thing is to accept incompleteness and provide static checking of the common cases: barewords and module name.identifier productions. Both of those tests are quite capable of skipping tests if module name is not a pure module. --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
The module author has the choice to re/structure his module to allow lazy binding/checking *iff the users demand*. All this means is that there will effectively be two different module systems, and in practice, every module provider has to pick one. Which is a problem, not a solution. Right - and this piles complexity on top of complexity, from both the user's point of view and the implementer's/analyzer's point of view. If you're going to demand such complexity, then you at least have to back it up with some real, concrete advantages. What are they? Bullet points are good. : ) Just so we agree what we're talking about, this is the default-default solution, right? ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On 27 Jun 2014 21:29, Andreas Rossberg rossb...@google.com wrote: Note that the other half of my argument was that real modules are only worth the complexity when they provide checking _consistently_. That is, it shouldn't matter whether I write import {f, g, h} from url f(); g(); h() or module M from url M.f(); M.g(); M.h() These should be freely interchangeable -- the programmer shouldn't need to pick between getting import checking but polluting the scope and making uses less readable, or the other way round. The current semantics falls short on that, it doesn't check in the latter case. So from my perspective, that would need to be fixed as well. Which would be fairly easy. /Andreas And this is yet another counter argument to the default export. If the default export is an object with properties (or a function with properties, which I quite common in npm) then there is no static checking of those properties. So with today's spec there is one way to get static checking and one ways (default) to not get static checking. Marius Gundersen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: Re: ModuleImport
Andreas, I hope you're not just getting my hopes up with the possibility of checking ModuleImports too. That would be great. I was disappointed that they weren't originally. Making module fs from 'fs'; fs.readFile(...); and import {readFile} from 'fs'; equivalent in terms of static checks is the right approach. If the cost of these improvements is not having default exports it seems acceptable. In essence we would have to write import {jquery} from 'jquery'; instead of import jquery from 'jquery'; The flexibility of not having to know the export identifier is worthless to me. I can't see how I could make use of a module without reading some documentation on it, which will show me the identifiers. Static checking and lazy binding should be value added features, not something I have to think about every time I import a module. I don't understand how you can make use of what you require without reading some sort of documentation or code. This will be an even smaller cost once tooling supports ES6 modules as it will allow autocompletion of imports statements. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Wed, Jun 25, 2014 at 4:50 PM, C. Scott Ananian ecmascr...@cscott.net wrote: @John Barton: Yes, ideally that syntax would work as well when you don't need a namespace. But sometimes you do need a namespace, even if you don't care precisely what it is: ``` import {format} from 'url'; import {format} from 'util'; import {connect} from 'tls'; import {connect} from 'net'; import {fork} from 'cluster'; import {fork} from 'child_process'; // etc ``` I agree, and importing as a namespace is what ModuleImport is all about. Crazy idea: what if we had this: // ModuleImport: import Identifier from StringLiteral import fs from fs; import url from url; And just got rid of the default monkey-business? Simple, no confusion, no refactoring hazards. Do we *really* need assignable default exports? If we could jettison that feature, it would (as John points out) make all of this pain go away. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Thu, Jun 26, 2014 at 8:15 AM, Kevin Smith zenpars...@gmail.com wrote: I agree, and importing as a namespace is what ModuleImport is all about. Crazy idea: what if we had this: // ModuleImport: import Identifier from StringLiteral import fs from fs; import url from url; And just got rid of the default monkey-business? Simple, no confusion, no refactoring hazards. Do we *really* need assignable default exports? If we could jettison that feature, it would (as John points out) make all of this pain go away. That would simplify the usage of modules (users wouldn't need to look up which of the two ways the module author (arbitrarily) decided to use) and it would simplify authoring of modules (only one way to export things). The only real complaint we are likely to hear is that it would not allow single export modules, which is the favoured usage of modules. I disagree with this; a module that only exports one named thing is a single export module. If you want to author a single export module then you only export one thing, and users of your module only import one thing: ```js //file AbstractSingletonProxyFactoryBean.js export class AbstractSingletonProxyFactoryBean { //... } //file MyApp.js import {AbstractSingletonProxyFactoryBean} from AbstractSingletonProxyFactoryBean; ``` Now the author can choose to export more things later without making breaking changes to the module. The only downside to this is the (apparently mandatory) curly braces around the imported object. If single export/import becomes the convention with ES6 modules then users will be forced to type an extra pair of {} several times in most of their files. Is the two extra characters something we can live with? Marius Gundersen ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Now the author can choose to export more things later without making breaking changes to the module. The only downside to this is the (apparently mandatory) curly braces around the imported object. If single export/import becomes the convention with ES6 modules then users will be forced to type an extra pair of {} several times in most of their files. Is the two extra characters something we can live with? I have a good bit of experience coding ES modules, and I was worried about that at first. But hasn't been a problem for me. Then again, I'm just one person - it would be good to get more data from developers actually coding with ES modules. This syntax would make things completely obvious and simple though: if there's curly braces, then you're reaching into the bag and pulling out things, and if there's no curly braces, you're getting the bag itself. There's even a visual analogy at play here: the curly braces themselves resemble the sides of a bag. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
Now the author can choose to export more things later without making breaking changes to the module. The only downside to this is the (apparently mandatory) curly braces around the imported object. If single export/import becomes the convention with ES6 modules then users will be forced to type an extra pair of {} several times in most of their files. Is the two extra characters something we can live with? I have a good bit of experience coding ES modules, and I was worried about that at first. But hasn't been a problem for me. Then again, I'm just one person - it would be good to get more data from developers actually coding with ES modules. This syntax would make things completely obvious and simple though: if there's curly braces, then you're reaching into the bag and pulling out things, and if there's no curly braces, you're getting the bag itself. There's even a visual analogy at play here: the curly braces themselves resemble the sides of a bag. To me, though, that goes back to the destructuring/not destructuring aspect. Maybe this has floated by during the bikeshedding, but why not something like: //import a single named export import foo from bar; //import multiple named exports import foo, baz from bar; //alias an imported named export import foo as fooAlias from bar; //import the module import bar as bar; So basically, just get rid of the {} for importing named exports, and move the whole module import after the as. It reads better to me and I think is more intuitive. As for default exports - I think they only make sense if done the way it works in node. A single default export that effectively replaces the module. Let's remember how it gets used in node: var _ = require(underscore); In node, if they didn't do it as a single export, then you would have to do: var _ = require(underscore)._; Given that underscore (and jquery and several others) are only a single export, it would be annoying and error prone to do that all over the place. The value added was for the importer - *not the exporter*. With the new syntax I'm proposing, importing underscore would be exactly the same as what the current proposal is for default export imports. import _ from underscore; Its just that underscore would have to have the named export _. As I said, though, that's not really a burden, and the argument for default exports was never really for the module writer. There is one small case I'm missing, which is that default export imports let you name the import whatever you want without writing extra code for the alias. I don't really find that as a drawback, but then again, I do write a lot of Java in addition to JavaScript. I don't really see any other languages that operate this way either, and I would come back again to the distinct history and constraints that JS/node has had up until now. If keep that flexibility/matching semantics for smoothest continuity, I would propose that default exports have to be a *single* default export, and that it would replace module importing instead of export importing. import underscore as _; Where there is a single default export in the underscore module. This would mean that the normal module import semantics don't work in this case, but that is the tradeoff decided by the module author. I personally don't think its worth adding this feature, but if it were added, I would expect it to work this way. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Thu, Jun 26, 2014 at 10:50 AM, Russell Leggett russell.legg...@gmail.com wrote: To me, though, that goes back to the destructuring/not destructuring aspect. Maybe this has floated by during the bikeshedding, but why not something like: //import a single named export import foo from bar; //import multiple named exports import foo, baz from bar; //alias an imported named export import foo as fooAlias from bar; //import the module import bar as bar; I like the fact that this doesn't look like destructuring, since variable binding is different from destructuring assignment. Could ``` import foo, baz from bar as bar; ``` allowed simultanous import of named exports and the module itself? If so, the grammar gains a pleasing regularity. OTOH, I think this syntax reorganization is orthogonal to some of the other issues discussed. In particular, your latter proposal still allows for user confusion between: ``` import _ from underscore; import underscore as _; ``` The various proposals seem to try to address this by making these semantically identical (or near-identical, with some cooperation from the module author). --scott ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss
Re: ModuleImport
On Thu, Jun 26, 2014 at 9:50 AM, Russell Leggett russell.legg...@gmail.com wrote: To me, though, that goes back to the destructuring/not destructuring aspect. Maybe this has floated by during the bikeshedding, but why not something like: //import a single named export import foo from bar; //import multiple named exports import foo, baz from bar; //alias an imported named export import foo as fooAlias from bar; //import the module import bar as bar; So basically, just get rid of the {} for importing named exports, and move the whole module import after the as. Yes! I would SO much happier with this if the curly braces were removed. -- R. Mark Volkmann Object Computing, Inc. ___ es-discuss mailing list es-discuss@mozilla.org https://mail.mozilla.org/listinfo/es-discuss