ES Modules: suggestions for improvement

Sam Tobin-Hochstadt samth at
Tue Jul 24 13:49:03 PDT 2012

On Tue, Jul 24, 2012 at 1:11 PM, Claus Reinke <claus.reinke at> wrote:
>>>    Here I've come around to Isaac's opinion that 'import *' is a
>>>    step too far. Previously, I said this is a convenient bad habit
>>>    that might be left to linters. But that was based on experience
>>>    with statically typed languages, where modules and their
>>>    import/export interfaces could still be analyzed in separation.
>>>    In ES, that is not the case: if 'System.set' and 'import *' are
>>>    combined, humans and tools would have to *run* dependencies
>>>    to discover the import interface. That makes it impossible to
>>>    analyze/understand such modules in separation, statically.
>> This is not correct.  You can look at a single module in isolation,
>> and learn exactly the same things about its interface that you can in
>> Haskell, for example.
> Haskell isn't a good role model wrt module systems - the main
> design goal there was simplicity, so it doesn't use advanced module system
> ideas (at least not in the standard module system). Also, some good aspects
> have disappeared, and some aspects haven't quite scaled up with the
> increased use.
> One thing that disappeared (because it wasn't done well) was interface
> files, which allowed to develop modules wrt module interfaces rather than
> module implementations. So, yes, Haskell suffers from a combination of
> 'import * from M' with no easy way to pin down M's expected export
> interface.
> Standard ML, and variants that support higher-order or even
> first-class functors (parameterized modules), might be more
> interesting in this context. Even when it can't be statically
> (before running the module-level code) determined which module will provide
> the imports, one can pin down which interface that module will provide. So
> one can understand each module in isolation, with the import and export
> interfaces acting as boundaries.

There's a *lot* to say about module systems in Haskell, ML and other
languages, but we *really* don't want to try to make JS push the
boundaries on what can be effectively statically checked.  The current
design adds a very small amount of static information and checking to
modules, and leaves open room for some more. The SML module system,
while impressive, is most certainly not the goal.

> But we can stay in ES6 for this discussion - consider
> <script>
> System.set('X',(Math.random() > 0.5) ? {x:"hi"} : {u:"oops"};
> </script>
> <script>
> module M1 {
> import * from X;
> console.log(x.length);
> }
> module M2 {
> import {x} from X;
> console.log(x.length);
> }
> </script>
> I cannot look at 'M1' and know whether or not 'x' is bound,
> because the import interface is unspecified. So I'd have to
> look at 'X' and, in this case, I'd have to run 'X' before I could
> tell whether 'x' in 'M1' is going to be defined.
> In contrast, I can look at 'M2' and know, because the import interface is
> specified, that, if 'X' is accepted as dependency for 'M2', then 'x' will be
> available. There might still be a
> problem at runtime, but it will be outside 'M2', and it will be
> about matching an export interface to an import interface,
> not about static scoping in 'M2'.

First, that dynamic module construction is going to be hard to reason
about -- that's the price we pay for all the great things about a
dynamic language.  Second, if you want `M2`, we're making it possible,
even easy, to write.  But I don't think we should ban people from
using `import *` because sometimes it's hard to reason about.

> (Such dynamic module aspects are another reason why one
> wants to keep exported, imported, and local names separate.)
>>>    In brief, in the context of a language as dynamic as JS, the
>>> convenience of 'import *' is not worth the damage it does to
>>> modular program understanding. Instead, we should ensure
>>>    that import interfaces are clearly and statically defined.
>> I disagree.  Clearly and statically defined interfaces are a great
>> thing for some software.  Other programs, be they scripts written by
>> middle school kids or dynamically-reflective towers of
>> meta-programming, don't want or need them.  What's the interface to `$`,
>> in the face of jQuery plugins -- you can't tell, statically.  But that
>> doesn't mean plugins are a bad thing.
> The question is not the export interface of '$', which can change
> with every plugin or new release. The question is whether importers of '$'
> can isolate themselves from such changes by specifying an import interface.
> Any export interface that provides the import interface will do.

Right now, jQuery works without its users specifying such an
interface. Again, people who want to specify such an interface have
their reasons, and we want to support them.  But people who don't
should also find a home in the design.

>>>    import .. from 'loader!resource'
>> Dave and I have been talking about this, and fortunately it doesn't
>> require changing the core elements of the module system -- it just
>> means making the `System` loader somewhat more configurable at
>> runtime.  Then you'd be able to specify what the 'text' loader should
>> do, and it would automatically hand 'text!resource' off to that
>> loader, using the existing module loaders mechanism.  This wouldn't
>> reduce any of the benefits we get, as Dave listed earlier, but would
>> allow us to express the sorts of things you can do in AMD with loader
>> plugins.
> Great!-) From what I could see of the discussion, this should
> remove the main technical obstacles raised against upgrading
> to ES6 modules.
> The translate hook should allow for things like Coffeescript or Streamline.
> The fetch hook ought to help with alternate sources (CDN with local
> fallback). The resolve hook ought to allow something like the RequireJS
> config (mapping abstract
> module names to concrete resources in a central position).

That's the idea.
sam th
samth at

More information about the es-discuss mailing list