ES Modules: suggestions for improvement

David Herman dherman at
Wed Jun 27 11:51:50 PDT 2012

On Jun 27, 2012, at 11:15 AM, Isaac Schlueter wrote:

> That was a bug caused by a lack of global isolation, which current
> module systems cannot fix.

It was caused by accidentally creating a global variable instead of a local variable. Not totally sure what you mean by global isolation? If you mean giving separate modules separate global objects, I don't agree that that would solve this kind of bug. He doesn't show us the whole code, but it looks like it was local code that was accessing the (accidentally) global variable, but probably different event handlers were interleaving and causing data races.

> I think we all agree that global isolation is the core purpose of a
> module system.  (Is that incorrect?)

Partly agree? I believe that obviating the *need* for globals is the core purpose of a module system. I don't believe that modules should necessarily be strictly separated. Modules should be given clean local scopes so that they don't overwrite each other, but that doesn't mean they shouldn't be able to still communicate via the global object.

> The question was whether there are in-the-wild bugs caused by typo-ing
> export names in current module systems.

That bug was particularly bad because it was *assigning* to an accidentally global variable. But in my personal experience I certainly forget to import common libraries like 'path' and 'fs' in Node all the time and end up with unbound variable references. When this happens in a control flow that got missed by tests, then it can end up in production.

>> The client chose to use *. You don't have to use * if you don't want to.
>> It's a convenience.
> It's unnecessary, afaict, and causes demonstrable harm in languages
> that have it.  If it's just a convenience, then it should be cut out.

You're not alone in this opinion. I disagree, but I think it's largely an orthogonal question.

> Something like this?
> import {myX: x, myY: y, z} from "foo"
> // comparable to:
> let {myX: x, myY: y, z} = require("foo")

Right, except flipped, as you said in your followup.

> Does this allow any way for the "foo" module to export *just* a single
> thing, as the top level result?  How would this be expressed?
> var Foo = require("foo")
> var f = new Foo()

Just import it directly:

import Foo from "foo";
var f = new Foo();

> If the answer is "that's not supported", then I think that's a
> significant gap.  It encourages a "one module = one thing" style and
> is very easy to reason about.  It would be better to give up
> multi-exporting in favor of exporting one thing, only.  If I could get
> away with making that change in Node, I would have by now.

I just disagree. I think it's fine if you like that style, and you can use it. But we shouldn't force it on users.

Moreover, it would be hostile to adding static constructs in the future, such as macros, that can be exported from a module.

> How does this proposal address transitive dependency cycles?

Better than yours. ;-P

> Unfinished export objects?

The exports are all there from the beginning but uninitialized.

> All of the problems that I'm bringing up, which you're saying are
> solved by the Harmony:Modules proposal, is it possible to solve them
> with less new syntax and boilerplate?

Maybe! Happy to discuss. I don't believe there's that much boilerplate. In fact, there's *less* boilerplate than either CommonJS or AMD, and compared to your sketches on your post I suspect the differences in character count could be counted on one hand.


More information about the es-discuss mailing list