Module naming and declarations

Andreas Rossberg rossberg at google.com
Thu May 2 11:13:38 PDT 2013


On 1 May 2013 01:15, Sam Tobin-Hochstadt <samth at ccs.neu.edu> wrote:
> On Mon, Apr 29, 2013 at 9:34 AM, Andreas Rossberg <rossberg at google.com> wrote:
>> I brought up [removing module declarations] as a "min-max" sort of compromise at
>> the March meeting, but it did not get much love. And I agree that it
>> isn't pretty, although it actually has some technical advantages, e.g.
>> enabling parallel parsing.
>
> No, that isn't correct.

What isn't correct?

> There are several reasons why module declarations are important.
>
> 1. Right now, every JS construct can be written at the REPL, in a
> script tag, in a bunch of files in a Node package, or anywhere else.
> This is a very important property for the language, and one we should
> maintain.  Without module declarations except as separate files, we
> would lose this uniformity.
>
> 2.  Being able to define multiple modules concisely in a single file
> is simply more convenient than having to separate them into multiple
> files. Even clunky old Java allows this simple convenience!
>
> 3. Serving modules as script tags, and defining modules in inline
> script tags, is likely to be important for a wide variety of use
> cases.
>
> 4. Concatenation.

I generally sympathise with (1). However, modules arguably are a
borderline case. They fall into the same category as multiple scripts
-- a very module-ish "construct" that cannot accurately be expressed
in pure JavaScript either.

As for the others, shrug. All valid points, and I agree that that it's
suboptimal if you cannot do that. On the other hand, nothing will
break, nothing _essential_ would be missing. That's the nature of a
min-max path.

But my broader point, of course, is that lexical modules address all
these cases just fine.


>> I do think lexical names are essential to provide internal naming,
>> which is vital. Thus having lexical modules was the first half of my
>> suggestion. "Public" modules however are externally visible, so are
>> naturally named by external names. The second half of my suggestion
>> was that we should use proper URIs for that. The "third half" was
>> about properly separating these concerns.
>
> Using your terminology, the current design uses names like "jquery/ui"
> for internal naming.  You're suggesting that we really need another
> level of internal naming, based on lexically-scoped identifiers.

Yes, because the current design necessitates (ab)using external names
for internal naming -- with all the problems that implies.

> This would be a coherent addition to the system, but is not a
> necessary one. Below, you point out some issues that would be
> potentially addressed by adding them.  However, multiple JS module
> systems are in use today, and with the partial exception of the
> TypeScript namespaces feature (which is a significantly different
> design), none of them has lexically-named modules in the sense you
> advocate.

I do not think this observation is particularly relevant. As I said
already, emulating a module system within JS necessarily has much more
severe limitations than designing it as a language feature. In
particular, I'm not sure you even _could_ build a lexical module
system under those constraints. There is no reason to apply those
limitations to ES6 modules, though.


>>> Also, the incoherent programs you refer to are ruled out by a
>>> suggestion (of yours!) that we adopted at the last meeting:
>>> declarative module forms that define a module that already exists are
>>> a static error.
>>
>> The suggestion you mention rather deals with the next case (clashes
>> between different internally defined modules). It does not generally
>> help when embedded resources clash with externally defined resources.
>
> When the second resource loaded is a declarative form, then this
> addresses this case as well.

Yes, but not the other way round.


>>>> * "Local" references are globally overridable.
>>>
>>> This is only true in the sense that executing first lets you define
>>> the meaning of particular modules.  But this is a *feature*.  This is
>>> just another way to describing configuration.  It's not reasonable to
>>> think that we can decide for everyone what will need configuration and
>>> what won't.
>>
>> Er, I'm not saying that _we_ should decide it. I'm saying that the
>> implementer of a module should be able to make that choice. In the
>> current proposal, he doesn't have a choice at all, he is _forced_ to
>> make each and every module definition essentially "mutable" by the
>> rest of the world.
>
> There are two possible scenarios here. If your module is loaded inside
> a loader that you don't trust, you have _zero_ semantic guarantees.
> The translate and link hooks give the loader arbitrary power, lexical
> modules or no.

Sure.

> The initial loader in the JS environment is mutable and
> thus if you are in a mutually-untrusting situation, you can't rely on
> anything.

Well, in practice it's not all-or-nothing. In this scenario, I worry
about accident more than malice. Once modular JS applications grow in
scale, and we get more interesting towers of libraries, I'm sure this
_will_ become a problem.

> In contrast, if you _can_ trust the loader, and the loader _isn't_
> exposed to arbitrary mutation, then you're in a much better position.
> After two modules are linked, the link never changes. Linking is a
> configurable process, but its *results* are immutable. This is in
> contrast to how JS programs that use the top level as a namespace work
> today, for example. Thus, two modules in a script tag or file that are
> linked together are just as bound as they would be in a system with
> lexical modules.

True. Except that in a larger application, loading will likely be
delayed for many modules, so the difference becomes a bit more blurry.

> Needing to explicitly register a module will certainly be common in a
> node-like system, where small modules are the norm. Which is the
> common case in general isn't a question we can answer today.  But I
> don't think that needing to *avoid* registering will be common.

Pro JS code today certainly goes to great length to avoid polluting
the global name space, by using anonymous functions. I think the
situation is much more similar than you'd like it to be.

But ultimately, it's simply a question of the right default. Making
everything externally visible, let alone overridable, is, plain and
simple, the wrong default. I'm still puzzled why you'd argue
otherwise, since for very good reasons, you made the opposite choice
one level down, for the contents of a module. I fail to see the
qualitative difference.


>>> While it's sad that everyone doesn't use the same filesystem ;), this
>>> is inevitable unless we force everyone to write *both* internal and
>>> external names explicitly for every module; that's clearly more
>>> user-hostile than the alternative.
>>
>> But you do not want to do that for every module! In fact, you rarely
>> need to explicitly define a module with an external name at all, at
>> least as I envision it. You only want to do that for a module that you
>> want to "export" from your current script, so to speak. Such "export"
>> (i.e., globally registering a module you defined textually) should be
>> a very rare operation to write manually (as mentioned, you don't
>> usually do the equivalent in AMD, for example).
>
> Anonymous module defintions are often used in AMD for modules that are
> *implicitly* named by the file they're in.  In ES6, that simply
> doesn't require a wrapper.

Yes, my point? Thing is, in AMD experience, named module definitions
are neither common, nor recommended. You use separate files. Why
expect something different for ES6?


>>> The reason to use concatenation is to avoid consuming excessive client
>>> resources -- in this setting of course you won't want to run
>>> translation on the client side.  Translation hooks are important (a)
>>> in less perf-sensitive settings like development and (b) for isolating
>>> remotely-loaded code, neither of which require concatenation.
>>
>> I don't believe it's as clear cut, and I can imagine a number of cool
>> use cases for translation that do not necessarily fall into this
>> simple pattern.
>
> Can you say more about these use cases?

For example, imagine statically generating (the definition of) a
non-trivial JS data structure from a domain-specific description
language and some dynamic information. Or things like FFTW. In a
similar vein, you could implement a semi-dynamic macro system on top
of JS.

/Andreas


More information about the es-discuss mailing list