Jussi Kalliokoski jussi.kalliokoski at
Wed Jul 2 01:09:20 PDT 2014

On Tue, Jul 1, 2014 at 10:28 PM, Kevin Smith <zenparsing at> 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
* (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: (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
to see how widely used the pattern of wrapping a module load in a try-catch

Now let's look at some things that tip the odds into existing solutions

* 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:


> _______________________________________________
> es-discuss mailing list
> es-discuss at
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the es-discuss mailing list