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=javascript&q=try+require&ref=cmdform&type=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: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. You can do whatever with the exports: enumerate, extend your own exports with them, whatever you want, it's just an object. In debugging situations and learning of new libraries, it's really straightforward to just require the module and give it different inputs to see what the outputs to see if it's something you want to use or what's the edge case of the method that causes the bug you're trying to fix (and then just make a failing test out of it). I can't tell you how many times a day I write angular.element(document.body).injector().get("Something") or require(function (Something) { window.Something = Something }) in the dev tools. With the current design of ES6 modules, I don't think it's even feasible to allow the users to type into the devtools console first `import something from "somewhere"` and then in the next entry refer to `something`. Not to mention that the dynamic loading in the existing solutions caters for situations like build tools, for example loading grunt tasks. The case there is that a lot of grunt plugins have dependencies lists from here to the end of the world, so when you run a task, you only want to load the modules that are required by that task to avoid concatenating files taking 20 seconds because it loads a ton of crap for other tasks. So people started loading the modules when the tasks get called, not all at the same time when initializing and this proved to have significant performance benefits. So much that now there's jit-grunt that by guessing and configuration is able to load the modules for a given task when the given task is invoked. This is simply not possible with ES6 modules without a massive boilerplate spaghetti with async stuff. 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. Now I can guess you're thinking that this was purely destructive, so where's the constructive part? The key takeaway is that I'm no longer sure that we should even try to ship modules with ES6. On the web platform, it's better to not hurry shipping something that's just going to end up on the wall of shame of short-sighted decisions. Brendan probably knows this the best. :) Let's take the time to actually ship something that we can confidently say is better than the status quo rather than doing this: http://xkcd.com/927/ Cheers, Jussi > > _______________________________________________ > 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