-
-
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') | |
@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?
Hey Luke… I definitely see the appeal of your proposal. Some of the benefits:
=
signmodule('a').b.c
, without the weirdness of'a'.b.c
or requiring two separate statementsimport
as the binding formMoreover, 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:
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 animport
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 sayimport X.Y.Z;
what happens ifX.Y.Z
defines a module calledX
? 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 ofimport
. That way you could compute the set of names in a lexical scope in two passes: first collect all themodule
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:
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