Module naming and declarations

Andreas Rossberg rossberg at google.com
Wed May 15 10:42:27 PDT 2013


On 14 May 2013 02:37, David Herman <dherman at mozilla.com> wrote:
> On May 10, 2013, at 7:18 AM, Andreas Rossberg <rossberg at google.com> wrote:
>
>> Can you explain how one form of module declaration is easier to "move
>> around"? In a single script there surely is no difference.
>
> Clients of a module can write:
>
>     import { f } from "foo";
>
> and regardless of how the module "foo" is shipped -- in a separate file or with an explicit module declaration in a bundled file -- the client code is unperturbed. This means that a single package can easily be deployed with any number of files without affecting client code.

I don't believe that is true. If the module is moved either from or to
a bundle file (and what else should such a move consist of?) then you
typically will have to do at least one of the following:

(1) Change the set-up of .ondemand calls.
(2) Change the invocation of your bundling tool.

As soon as you have to go there, you've lost almost all advantages of
the ID-based declaration form. Its assumed convenience doesn't scale
to non-trivial scenarios.


>> OK, perhaps I am under wrong assumptions about the semantics of
>> ondemand. I assumed that when you first import a module that has been
>> registered via ondemand, the respective script will be executed,
>> recursively, and then linking of the outer script continues, taking
>> the updated loader environment into account.
>
> OK, this is a big difference between your imagined system and the current one. The current system does not execute modules until dependencies have been fetched and linked -- I explained this in the March meeting. (The .ondemand method is sort of a distraction; it's nothing more than an API convenience layered upon the underlying resolution hook.)

Wait, I talked about the script registered for .ondemand itself, i.e.
its toplevel, not the modules that it contains. The script itself is
not a module.

And no, I don't think .ondemand is a distraction at all! It is the
core mechanism to make bundling work (other than separate script
tags). And that has considerable implications on module declarations
themselves, which I'm trying to pin down.

> This means that cyclic dependencies work, and dependencies are resolved statically across multiple files, causing a full static dependency graph to be concurrently fetched.

I agree about (cross-bundle) cyclic dependencies, but no package
system I'm aware of in any language or system supports cross-package
cycles. We seemed to agree that such recursion isn't relevant.

On the other hand, I don't see how parallel fetching is affected, nor
how your statement about fetching the full dependency graph in
parallel is ever true. Correct me if I'm wrong, but either way, you
can fetch the ondemand script itself in parallel with other (already
known) imports. And either way, any import that (used) modules defined
in that script need will require another round of fetching.

The only real difference is at what point you _execute_ the script
itself, before that other round of fetching or later. A bundle script
shouldn't usually have much toplevel code in it, so its execution
shouldn't matter too much for the relevant use case, and I could see
it happen in the middle. Also, we had been discussing interleaved
execution for legacy imports already, so this does not seem different.

I do realise now, however, that it gets uglier when an import triggers
_multiple_ ondemand scripts at once, because then their execution
would have to be sequentialised.

In any case, I'm afraid I still don't know what exact semantics you
intend for ondemand. Can you please clarify? When is the script body
executed? And what effect do updates to the loader during execution of
that script have?

Fortunately, I think there is a fairly easy solution that obsoletes
string-based declarations, while avoiding sequentialised execution (or
execution at all) during fetches. More on that later.


> What you describe has a sharp distinction between packages and modules,

Yes it has! And that's a feature. ;)  Because:

- intra-package module references should be internal and fixed,
- inter-package module references need to be external and configurable,
- packaging should mean constructing a larger package from a set of
smaller ones by turning (a subset of) its external references into
internal ones.

(You are not tied to this scheme with what I propose, but that's what
you usually want.)


> where packages cannot have cyclic dependencies, must be dynamically loaded and registered, and are composed only of lexically scoped modules. What this seems to mean is, to implement a package (such as a library, application, or component of an application), you have to choose from one of three options:
>
> - implement the package in one big file.
> - implement the package in multiple files via some extension to ECMAScript (e.g., include) that requires a tool to assemble it back together in a single file with only lexical modules.

Why would that require an extension? Import from URL is just fine for
that purpose. And when you deploy, you run the tool, see above.

> - split the package into smaller packages, each comprising only one or at least very few modules, forgo any cyclic dependencies, and effectively get little to no benefit from lexical modules.
>
> Please do tell me if I'm missing something, because all of the above scenarios seem obviously impractical. In particular, it's a necessity that the system should work well out of the box, with no additional tools. Obviously large and sophisticated applications will be willing to buy into additional infrastructure, but you should certainly be able to put a package's modules in separate files without build tools.

Counter question: what other options are available in the current
design, under your no-tool and no-staging assumption? AFAICS, you can
do either of the following:

- Write script files with module declarations. Then you can
concatenate naively, but you cannot import as is without staging
(setting up .ondemand for each file).

- Write module files. Then you can import without staging, but you
cannot concatenate naively.

I don't envision many people would want ever to use the first option,
it's neither natural nor convenient. Nor does it provide a significant
advantage over what you do lexically. And the other option requires
tool support. And that's what I have tried to argue in the last couple
of posts: that your system does not really work without tools either.
You cannot "concatenate" module files without a tool.

Once you commit to using a tool, whether that does marginally more
rewriting or not is immaterial. At the same time, lexical modules make
the result more robust (plus they provide the other advantages I've
mentioned).

/Andreas


More information about the es-discuss mailing list