ES Modules: suggestions for improvement

Claus Reinke claus.reinke at talk21.com
Sat Jul 21 02:03:21 PDT 2012


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

    In other words, one could permit

        module M { export { x: .. , y: ..} }
        import {x,y} from M

    or

        module M { var obj = .. ; export obj}
        import obj from M
        .. obj.x ..

    but not

        module M { var obj = .. ; export obj}
        import {x,y} from M    // early error, even if obj.x/obj.y exist

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 '$'

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

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

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.

    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.

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

then perhaps

    import .. from 'resource' using 'loader'

might do (in both cases, 'loader' itself would be loaded as a
module).

Claus
 


More information about the es-discuss mailing list