Module naming and declarations

Sam Tobin-Hochstadt samth at ccs.neu.edu
Tue May 7 12:48:59 PDT 2013


On Thu, May 2, 2013 at 2:13 PM, Andreas Rossberg <rossberg at google.com> wrote:
> 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?

Sorry, I think I quoted the wrong thing here.

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

That multiple scripts can't be expressed in source is no reason to add
_more_ constructs like it.

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

My point was not to say that concatenation is a weak argument and that
we need more.  You agree that these are valuable features.  However,
concatenation support is a *required* feature. The world in which
multiple http requests are free is not here, and not on the horizon.

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

It's certainly possible to build a system like this in a compile-to-JS
language like Typescript, and I don't believe it's happened.

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

True, but I contend that these accidental clashes will be much more
likely with the declarative form -- using loader.set() is likely to be
less common and used more knowledgeably.

>>>>> * "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 the case of accident, just putting the two modules in the same bit
of code that's compiled together (such as a file) will prevent such
accidents. Across files, lexical scope won't be sufficient unless
you're using a build tool.

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

Mostly, this is managed by using a single namespace that everything
hangs of, as with jQuery. This strategy is easy to apply to the module
name space as well.

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

These are designed for different purposes.  Modules are intended to be
abstractions, module names are designed to help manage collections of
modules.

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

Right, you use separate files and that gives you a bunch of
string-named modules registered in the global modules registry.
That's then compiled to explicitly named modules all in one file. That
this is similar to our design should be unsurprising, since they're
working on the same problem, and both systems care about
concatenation.

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

These are great use cases, but if you're using them you'll have to
make a choice between separate files and string literals.  We can't
make design decisions based on assuming a packaging format for
multiple files that will exist in the hypothetical future.  If such a
system existed and was shipping already, things would be different.

Sam


More information about the es-discuss mailing list