-
-
Save lukehoban/3900992 to your computer and use it in GitHub Desktop.
Things I want to be able to do: | |
a) Bind a name to a remote module | |
b) Alias a name for a module | |
c) Alias a name for a member of a remote module | |
d) Put all names from inside a module in lexical scope | |
e) Put all names from inside a remote module in lexical scope | |
f) Put a few names from inside a module in lexical scope | |
g) Define a local module | |
h) Export names from lexical scope | |
i) Re-export names from other modules | |
j) Re-export all names from another module | |
Note that (d) and (e) are likely to be removed from ES6. Per @dherman comments below, (d) is possibly a non-goal while (e) is just a postponed goal. | |
// #1 – Current (A) | |
a) import 'a' as a | |
b) module b = X.Y.Z | |
c) import { X: c} from ‘ide/code’ | |
d) import * from X.Y.Z | |
e) import * from 'a'; | |
f) import { d, e: e2 } from 'a' | |
g) module X { } | |
h) export { d, e: e2} | |
i) export { K: a.K } // I believe it is not possible to do this without also having the corresponding import line (a) above | |
j) export * from X.Y | |
Pros: Mostly shorter | |
Cons: Lots of new syntax (module, import, from, as, =, *), feeling of inconsistency | |
// #2 – Current (B) | |
a) module a = “a”; | |
b) module b = X.Y.Z | |
c) import { X: c} from 'ide/code' | |
d) import * from X.Y.Z | |
e) import * from "a"; | |
f) import { d, e: e2 } from 'a' | |
g) module X { } | |
h) export { d, e: e2} | |
i) export { K: a.K } // I believe it is not possible to do this without also having the corresponding import line (a) above | |
j) export * from X.Y | |
Pros: Slightly more consistent than (A) | |
Cons: Still a fair bit of new syntax (module, import, from, =, *), still inconsistent, though less so | |
// #3 – An alternative proposal currently used by TypeScript | |
a) import a = module('a'); | |
b) import b = X.Y.Z | |
c) import c = module('ide/code').X | |
d) import X.Y.Z | |
e) import module('a'); | |
f) import { d, e: e2 } = module('a') | |
g) module X { } | |
h) export { d, e: e2} | |
i) export { K: module('a').K } | |
j) export * from X.Y | |
Pros: Consistent use of 'import' for all notions of import, limited new syntax (import, module, =) | |
Cons: Wordier with use of module('a') | |
Hey Luke… I definitely see the appeal of your proposal. Some of the benefits:
- consistency with the JS precedent of binding on the left-hand side of an
=
sign - the right-hand side defines a compositional "module path expression" language
- possible to select an export in place with
module('a').b.c
, without the weirdness of'a'.b.c
or requiring two separate statements - consistent use of
import
as the binding form
Moreover, I'm working on modifying the design so that local modules are named by strings rather than variables, and they go directly in the current loader's registry. This lets you do local modules like so:
module "foo" {
module "bar" { ... }
import { d, e: e2 } = module("bar");
}
I know @domenic and others think import *
is a tool of the devil, and it's off the table for ES6, but I still intend to future-proof for it, because I suspect people will miss it. So just thinking it through, allowing your syntax has the problem that it's impossible to tell syntactically whether an import
binding names a module or a variable. This may sound innocuous, but it becomes a problem for bulk import, because it becomes very hard to deal with the possibility that a bulk import might actually bind a module used in a module path. For example, when you say import X.Y.Z;
what happens if X.Y.Z
defines a module called X
? I won't go into details here, and there are many possible semantics, but they all have issues.
I had originally designed the syntax so that you could tell syntactically that a module binding was a module binding because it was always spelled module
instead of import
. That way you could compute the set of names in a lexical scope in two passes: first collect all the module
bindings and bring them into scope, then resolve all the module paths and bring all their exports into scope—no danger of paradoxes like the one I described above.
Perhaps a way to fix the paradox with a syntax like your proposal is to disallow bulk imports from module expressions that start with a lexical variable. So your e) would be legal but not d). This might sound restrictive, but if you consider that local modules would now be given string names instead of variable names, you can still do:
module "X" { ... }
import module("X");
The only thing being disallowed is bulk import from an aliased module name. That seems less problematic. So in other words, I'm suggesting the following variation on your proposal #3:
d) disallowed
e) possibly allowed post-ES6
g) module 'X' { }
There are some more use cases I'd be interested to hear your thoughts on:
h) Export names from lexical scope
i) Re-export names from other modules
j) Re-export all names from another module
I'm guess you'd propose something like:
h) export { x: x, y: y }
i) export { x: module('a').x, y: module('b').y }
j) export module('a');
Is that what you were thinking?
Thanks,
Dave
@domenic: Your a) doesn't actually do what a) is supposed to do, which is bind a name to the module itself, not one of its exports. Maybe you just don't really want to support a) since you believe module declarations should only be for concatenators. I imagine that's the major use case, although I think don't see why people wouldn't use local modules for lightweight code organization.
Dave
@dherman: Yes, your line of reasoning makes sense. Cases (d) and (e) are the two that I had thought least about so far, both because they are being postponed from ES6 and because TypeScript similarly has not incorporated any syntax for these cases. But I do agree that designing the syntax with these cases in mind is a good idea.
I've updated the gist with a version of your h,i,j. I believe the moral equivalents of (i) and (j) in particular weren't possible with a single export expression in the earlier syntax proposals on the wiki?
Only new syntax:
module
for declarations (should never be used except by browser concatenators);import x from y
for all your importing needs.