ES Modules: suggestions for improvement

Sam Tobin-Hochstadt samth at ccs.neu.edu
Mon Jul 23 11:56:17 PDT 2012


On Sat, Jul 21, 2012 at 5:03 AM, Claus Reinke <claus.reinke at talk21.com> wrote:
>>> If you want to export a bag of functions, then put the functions on an
>>> object, and export the object.
>>>
>>> It *is* making it trickier to figure out how to add types and macros,
>>> but I'm less excited about those features than I am about making our
>>> existing problems easier to solve.
>>
>>
>> It's not trickier, it's essentially impossible. If we don't support static
>> imports and exports, those doors are shut.
>
>
> 1. It should be possible to reconcile the two styles:
>
>    - if the single export object is an object literal, then one-level
>        early checking against imports should be possible
>
>    - if the single export object is not an object literal (eg, a function),
>        early checking could limit static imports to that single object
>        (the properties of which could still be selected dynamically).

On the subject of 'exporting one value', there are a few things to say:

1. It's been asserted repeatedly that libraries that export 'just one
value' are a better design, but while this is excellent style in lots
of cases, I don't think a persuasive case has been made that this
should be the *only* style.  Dave listed a number of key Node
libraries that don't follow this rule, and if you look at other
libraries or other languages you mostly see the same thing --
sometimes it's the right thing, and sometimes it isn't.

2. Exporting a value *as* the module runs into tricky issues in the
relationship between the prototype hierarchy, the exports, and the
definition of module instances.  For example, should exporting a
function as *the* export of a module named `M` mean that `M.call` is
also available?

> 2. Why the focus on an early -limited to one-level- check in the
>    current spec can seem non-optimal:
>
>    - if one wants to export a single object, one has to introduce
>        a level of indirection: 'import {theThing: thing} from module';
>        this is a workaround, for a newly design module system
>
>    - one level of indirection is sufficient to defeat the early checks:
>        'import {jquery:$} from jquery' give no guarantees whatsoever
>        about the components of '$'

JS is a dynamic language -- there's no way we're taking away the
ability to export JS objects from modules, so I don't see what this
objection is about. You could write your whole program in a string,
and give up early syntax errors, too.

> 3. There is the question of explicit module export interfaces:
>
>    - in ES5, a single explicit export object (as an object literal) makes
> the export interface obvious; while assignments to exports don't
>
>    - in ES6, export declarations are syntactically limited so that tools
>        can unambiguously identify (one level of) the export interface;
>
>        that doesn't mean that humans should have to hunt for export
>        declarations spread throughout the module, though

You can use one `export` declaration at the top of the module, but
we're not requiring that style.

> 4. The same question arises for import dependencies:
>
>    - in ES5, scanning for calls to 'require' and their parameters gives
>        no guarantees if conventions are not followed
>
>    - in ES6, 'import' conventions are enforced statically, so tools can
>        discover dependencies statically; again, humans should not
>        have to hunt for 'import' declarations

I don't understand you point here.  The point of the module system is
not to mandate one preferred code organization style.

> 5. and for import interfaces:
>
>    - ES5 modules make no guarantees
>    - ES6 modules rely on static checks so hard that they allow
>        'import *' to interact with the importer's lexical environment
>
>    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.

>    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.

>> Not to mention the other things I mentioned in my blog post, including
>> straightforward optimizations and interoperability with modules written in
>> other languages.
>
>
> As others have tried to point out before, and I've tried to pin down in the
> thread
>
>    ES modules: syntax import vs preprocessing cs plugins
>    https://mail.mozilla.org/pipermail/es-discuss/2012-July/023985.html
>
> current dynamic JS module systems (both AMD and node's) handle language
> interoperability and preprocessing in ways that the currently spec-ed ES6
> modules cannot:
>
> While all three systems provide for loader plugins in some
> form, ES6 modules currently make it very hard to use such
> plugins, requiring a switch away from the new static module
> system to dynamic and asynchronous features.
>
>
> The solution seems straightforward, and has been championed
> by James here: allow loader plugins to be specified on import,
> without leaving the new world of static and syntactic module
> imports. If you don't like
>    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.
-- 
sam th
samth at ccs.neu.edu


More information about the es-discuss mailing list