Module naming and declarations

Andreas Rossberg rossberg at google.com
Wed May 22 03:22:54 PDT 2013


On 18 May 2013 09:12, David Herman <dherman at mozilla.com> wrote:
> On May 15, 2013, at 10:42 AM, Andreas Rossberg <rossberg at google.com> wrote:
>
>> (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.
>
> No. You've missed the point. Configuration code is not the problem; there's at most a 1:1 relationship between modules and their configuration.
>
> There is a 1:many relationship between a module and import declarations that refer to it. This is what matters: even if you reconfigure the app to change the way the module is shipped, every client module's import declaration should remain the same. This may include repackaging, file renaming, switching between CDN's, switching between versions, etc.

Agreed to all of this. But it doesn't contradict what I said. I did
not state that you shouldn't be using some form of logical name (at
the package boundary). I said that "moving stuff around" is not free
with them either.

If all you meant earlier is that the logical name as such doesn't need
to change when you move the underlying definition then we're on the
same page. But that is independent of how you actually register
logical names.


> The execution semantics goes roughly like this: once all the module dependencies are computed and fetched, the linking process begins. If there are no module-factories (i.e., the AMD-style modules that require their dependencies to have been executed before they can compute their exports), linking is observably atomic. Then the bodies (script bodies and any as-yet unexecuted module bodies that have any clients importing from them) are executed sequentially. If, however, there are module-factories, the process is more interleaved: the system atomically links all declarative modules transitively required by each module factory, then executes those declarative modules, then executes the module factory, rinse, repeat.

I see. I was thinking of ondemand scripts as "module factories". (Is
that a new term? I haven't seen it used before.)


>> - intra-package module references should be internal and fixed,
>
> You keep making this claim as if there's some black-and-white distinction. When we pointed out that your intra-package references are not tamper-proof given the translate hook, you said "well but in practice..." So, that argument cuts both ways.

Hold on. You and Sam are the ones who keep bringing up the
black-and-white "but the loader can do anything anyway" argument.

My view is more differentiated: there is a spectrum of
"tamper-proofness" or robustness or integrity. On that scale, lexical
scoping > physical external names > logical names in a sandbox >
logical names in a world with free write access to the loader. At the
high end, tampering is only possible for somebody with high-trust
capabilities (who defined the loader), whereas at the low end, any
dork can mess with everything.

It's good practice to place as much of your code as high in this
spectrum as possible. Your proposal, however, puts _everything_ at the
bottom end. That's not only the default, it's the only possibility!
Don't you think that's strictly worse?


> In practice, intra-package references will not be messed with externally. They are only hookable at compile-time; once runtime starts they are completely hard-bound. And if there are any collisions, there will be a compile-time error anyway. This is a tempest in a teapot.

There is no generally useful distinction between compile time and run
time in JavaScript. Because of staging, eval, and all that. That makes
that a very weak argument.

As for the "in practice" part, we can just hope you are right. I, for
one, see a lot of potential for accident and malice there.


>>> - 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.
>
> It requires non-standard semantics if you want to allow cyclic module dependencies, and probably also if you want to preserve the same execution semantics as lexical modules.

Sorry, I don't follow. What should be the problem with cyclic import
through URLs?


>> 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...
>>
>> - Write module files...
>
> I never said staging is bad, I said the staging in your solution is broken: it over-sequentialize the fetching of resources.
>
> The requirement I'm talking about -- which is absolutely critical for the practicality and adoption of ES6 modules -- is that the system has to work well out of the box without tools. And the current system does. The only thing you have to change if you decide to repackage modules into scripts is a *single* piece of code staged before application loading that configures the locations of the modules.

If that single piece of code isn't entirely trivial then you don't
want to maintain it by hand. You want to generate it, in the same way
you want to, say, generate Makefile dependencies. Sure you can write
them down manually, but not for an application with many modules in
many packages.

All I'm saying is that "no tools" may work for small projects, but
does not scale to anything more serious.


> Note that this is how require.js works.

Require.js provides its "optimizer" for packaging. And AFAICT, that's
what people use in practice. I don't see them writing bundles by hand
much.


>> I don't envision many people would want ever to use the first option...
>> And the other option requires tool support.
>
> That's simply not true. Look at require.js:
>
>     http://requirejs.org/docs/api.html#config
>
> With even just a reasonably convenient ondemand, you can easily move some or all of your modules into scripts by hand. Even without ondemand, the resolve hook is straightforward. (BTW I hate the name `ondemand`; it'll get renamed to something more declarative-sounding, like AMD's `paths`.)
>
> Your suggestions would result in a system that is unusable without tools. That's not acceptable and it's not going to happen.

I don't know what specific suggestion you are referring to. I have no
intention of making the system less usable without tools. Rather, my
point is that for anything of a certain size you want tools anyway.
Certainly, you do not want to turn more than a couple of module files
into a script manually. Why would you?

/Andreas


More information about the es-discuss mailing list