ModuleImport

Andreas Rossberg rossberg at google.com
Fri Jun 27 06:07:19 PDT 2014


Some observations:

* I think the 'import * as x' syntax for module imports is not an
improvement, for at least two reasons:

  - It raises the expectation that you can actually write 'import *
from' (as already noted in this thread).

  - It removes the syntactic marker for binding a module identifier.
That is problematic because (a) module identifiers come with extra
static checks on their uses (or so I thought, see below), and it is
(b) future hostile to lexical module declarations, because with those,
only module identifiers could be used in certain contexts (e.g., on
the RHS of an import-from). Thinking forward, I think it would be
highly preferable to consistently mark all bindings of module
identifiers with a 'module' keyword.

* I think the controversy is because the module design tries pleasing
two incompatible community goals, but instead of achieving that, it
has acquired all the characteristics of a committee design (although,
ironically, modules are probably the least committee-designed part of
ES6 :) ):

  - Some want modules in the conventional sense, as encapsulated
namespaces with named exports, which you can import and access
qualified or unqualified, and where imports are checked.

  - Some want modules in the specific pre-ES6 JavaScript style, where
they can be any arbitrary JS value, and in the good old JavaScript
tradition that checking doesn't matter.

The current design (including the newest suggestions for imports)
seems to please neither side, because it tries to be the former
semantically, but wants to optimise for the latter syntactically. The
main outcome is confusion.

* This is probably the 3rd or even 4th time round this aspect of
modules is discussed controversially. That is not a good sign. I
_really_ think we need to make up our minds:

  - Either we think "real" modules are an improvement, and checking is
important. Then the model and the syntax should be consistent about
that. Moreover, checking needs to consistently apply, no matter how a
module and its components are defined or accessed.

  - Or we come to the conclusion that supporting the legacy singleton
export model as a primary use case is a mandatory matter. Then we
should drop attempts of building something in/semi-compatible, and
limit innovation to making export and import declarative, and
designing a loader API.

The current design falls between stools with respect to syntax,
leading to the confusion people complain about. And it falls between
stools with respect to import checking, making it dependent on
superficially syntactic choices (like, whether you use default export
or not, or whether you use unqualified import or not), and thereby
unreliable in practice.

Quite honestly, I rather have no import checking than checking that
only applies in some syntactic cases -- because that gives a false
sense of security, harms refactoring, and/or encourages overuse of
unqualified imports, hampering readability. And if we are willing to
cut partial checking, then there actually is much less compelling
reason to have all the machinery around module objects, import as
aliasing, etc. The system could probably be simplified quite a bit,
and get much closer to what proponents of legacy JS modules are used
to.

Of course, I'd much rather go with "real" modules. But more
importantly, the system should make a choice. Something in the middle
is looking more and more like the least attractive alternative -- it
seems like substantial extra complexity for too little (or even
negative) benefit. As surprising as it may sound to some, I'm starting
to warm up to the legacy option if it avoids the floor between the
stools. :)

/Andreas


On 19 June 2014 10:15, David Herman <dherman at mozilla.com> wrote:
> Thanks to everyone for working through the issues around ModuleImport. I know it's been frustrating, but the discussions have really helped clarify the key constraints.
>
> ## Constraints
>
> First, let me restate the most important arguments. In favor of removal:
>
> * **The syntactic distinction between ModuleImport and default import is vague.**
>
> We've consistently seen confusion between the semantics of ModuleImport and default export. While certainly some of the confusion might be chalked up to misinformation and lack of documentation, we've seen people be confused by this even when correctly explained the semantics. The problem with the syntax is that the only visual distinction is the initial keyword (`module` vs `import`), and it's not clear from that keyword which concept you're talking about. (Once you've internalized the structure of module instance objects, you get a hint from the `module` keyword, but that doesn't help people who are just learning the system, particularly if they're already familiar with other JS module systems.)
>
> Against removal:
>
> * **Without ModuleImport, authors of multi-export modules would be pressured to circumvent the named exports functionality.**
>
> Without ModuleImport, clients of multi-export modules have two options. Either they use named import:
> ```js
> import { readFile, writeFile } from "fs";
> ```
> or if they want to explicitly namespace those functions, they have to use the dynamic API:
> ```js
> import "fs"; // state the dependency
> var fs = this.get("fs");
> ```
> That's inconvenient, confusing, and makes you feel like you're stepping outside the normal usage patterns intended for the system. Since this is a choice forced on the clients of a module, the module author will feel pressure to circumvent the named export feature altogether and instead export a default object with all the properties. This is bad -- it creates pressure for people to abandon part of the module system.
>
> ## Conclusion
>
> Here's the conclusion I've come to based on all of the above.
>
> * **We need a form like ModuleImport.**
>
> As many have said, clients of named-export modules need the freedom to choose whether to explicitly namespace those imports, and they need a syntax that doesn't feel like they've stepped outside the normal system.
>
> * **The current syntax of ModuleImport is wrong.**
>
> The confusion reported in developer feedback is real, and it's important.
>
> * **The syntax should still favor default import.**
>
> ES6 favors the single/default export style, and gives the sweetest syntax to importing the default. Importing named exports can and even should be slightly less concise.
>
> * **A better ModuleImport syntax is possible, and we should settle it soon.**
>
> I'll propose a better ModuleImport syntax below. Syntax being syntax, everyone will of course unsheathe their bikeshed paintbrush. That's OK. The champions will keep it from going on unbounded and help settle the debate, and we'll make sure to capture the conclusion in the next TC39 meeting.
>
> I do acknowledge the concerns about reopening topics for debate and delay. But given the usability feedback, I think this case is worth fixing. We should resolve it for ES6, perhaps in part because it's less editorial work to change the ModuleImport production than to defer it, but more because I don't want to delay the resolution so that implementations can ship the better syntax. But keep in mind it doesn't matter what spec it lands in as long as implementations are shipping it. We're still in the early days of transpiler implementations, and there are no native implementations of modules yet. So there's time, as long as we don't let the bikeshed go on forever. So let's get to it!
>
> ## Proposal
>
> OK, so we're talking about a better syntax for importing a module and binding its named exports to a variable (as distinct from importing a module and binding its default export to a variable). Here's my proposal:
> ```js
> import * as fs from "fs"; // importing the named exports as an object
> import Dict from "dict";  // importing a default export, same as ever
> ```
>
> Why is this better? Because it dovetails with the named export syntax, **which makes it clear that it's binding `fs` to the named exports.** It saves the reader from having to know that the module instance object is not the same thing as the default export.
>
> So there you have it... Feedback welcome, and as always we'll collect it all, weigh it, and try to shepherd consensus.
>
> Oh, and just to put parameters on the discussion:
>
> * **I'm not proposing additional new import forms.**
>
> If this syntax is future-compatible with other possible syntactic forms, that's great, but that's not what we're discussing.
>
> * **I'm not interested in discussing changes to the rest of the module syntax.**
>
> This isn't the time or place to redesign the whole system.
>
> * **The `this` binding in modules is a separate topic.**
>
> The `this` binding in modules can be discussed independently of this topic.
>
> tl;dr we're only talking about the ModuleImport syntax here.
>
> Dave
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss


More information about the es-discuss mailing list