Module syntax
On Mon, Jun 3, 2013 at 1:29 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
Bikeshedding: I generally like the module syntax, but I’d always omit the braces and use
import default
for importing the default export of a module.
From the wiki:
module "foo" { export default function() { console.log("hello!") } } ... import default;
What would be the bound name? foo? Am I missing something?
On Mon, Jun 3, 2013 at 1:29 PM, Axel Rauschmayer <axel at rauschma.de> wrote: Bikeshedding: I generally like the module syntax, but I’d always omit the braces and use
import default
for importing the default export of a module.From the wiki:
module "foo" { export default function() { console.log("hello!") } } ... import default;
What would be the bound name? foo? Am I missing something?
I’d use: import default foo from "foo";
Thus, the examples in the wiki [1] become: import default $ from "jquery"; // import the default export of a module module crypto from "crypto"; // binding an external module to a variable import encrypt, decrypt from "crypto"; // binding a module's exports to variables import encrypt as enc from "crypto"; // binding and renaming one of a module's exports export * from "crypto"; // re-exporting another module's exports export foo, bar from "crypto"; // re-exporting specified exports from another module
[1] harmony:modules
On Mon, Jun 3, 2013 at 2:40 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
On Mon, Jun 3, 2013 at 1:29 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
Bikeshedding: I generally like the module syntax, but I’d always omit the braces and use
import default
for importing the default export of a module.From the wiki:
module "foo" { export default function() { console.log("hello!") } } ... import default;
What would be the bound name? foo? Am I missing something?
I’d use: import default foo from "foo";
How is this better then the existing:
import foo from "foo";
For importing a default export?
On Jun 3, 2013, at 20:41 , Rick Waldron <waldron.rick at gmail.com> wrote:
On Mon, Jun 3, 2013 at 2:40 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
On Mon, Jun 3, 2013 at 1:29 PM, Axel Rauschmayer <axel at rauschma.de> wrote: Bikeshedding: I generally like the module syntax, but I’d always omit the braces and use
import default
for importing the default export of a module.From the wiki:
module "foo" { export default function() { console.log("hello!") } } ... import default;
What would be the bound name? foo? Am I missing something?
I’d use: import default foo from "foo";
How is this better then the existing:
import foo from "foo";
For importing a default export?
It’s worse. The point would be to make non-default imports simpler and lose the braces (which make less sense with the new non-literal syntax) in those cases. That is: it makes default imports less convenient in order to make non-default imports more convenient and less grawlixy.
Compare – wiki: import $ from "jquery"; // import the default export of a module module crypto from "crypto"; // binding an external module to a variable import { encrypt, decrypt } from "crypto"; // binding a module's exports to variables import { encrypt as enc } from "crypto"; // binding and renaming one of a module's exports export * from "crypto"; // re-exporting another module's exports export { foo, bar } from "crypto"; // re-exporting specified exports from another module Brace-free: import default $ from "jquery"; // import the default export of a module module crypto from "crypto"; // binding an external module to a variable import encrypt, decrypt from "crypto"; // binding a module's exports to variables import encrypt as enc from "crypto"; // binding and renaming one of a module's exports export * from "crypto"; // re-exporting another module's exports export foo, bar from "crypto"; // re-exporting specified exports from another module Syntactic ambiguities may be a problem, though...
We may be able to have both: convenient default imports and convenient non-default imports, if we bring back export =
(which, IIRC, has been proposed a while ago) and introduce import =
as an analog. Example:
module "jquery" { export = { ... }; }
import $ = "jquery";
Not completely happy with it (as both look like assignments), but it complements the non-default case nicely and everything combined may be the best we can do overall.
Remaining changes – wiki: import { encrypt, decrypt } from "crypto"; // binding a module's exports to variables import { encrypt as enc } from "crypto"; // binding and renaming one of a module's exports export { foo, bar } from "crypto"; // re-exporting specified exports from another module Brace-free: import encrypt, decrypt from "crypto"; // binding a module's exports to variables import encrypt as enc from "crypto"; // binding and renaming one of a module's exports export foo, bar from "crypto"; // re-exporting specified exports from another module
I was corresponding with Axel about what's possible without ambiguity, and he pushed for brace elimination noting that 'default' is a reserved word. This led to me remembering the export = ... form, more concise than export default ... -- not that "more concise" must win, but it seems worth a brief discussion. The export = form also looks more like today's module.exports = ... from NPM/AMD.
I really dislike export =
. It looks like it should be creating a global variable named export
.
export default
is perfect IMO. It also conceptually fits better with how default exports work. (I.e., they're default exports, they're not the entirety of the exports. They don't overwrite the module instance object, in the fashion that module.exports =
does.)
On Mon, Jun 3, 2013 at 3:57 PM, Domenic Denicola < domenic at domenicdenicola.com> wrote:
I really dislike
export =
. It looks like it should be creating a global variable namedexport
.
export default
is perfect IMO. It also conceptually fits better with how default exports work. (I.e., they're default exports, they're not the entirety of the exports. They don't overwrite the module instance object, in the fashion thatmodule.exports =
does.)
Agreed. I was drafting a response when this came through and it expresses everything I was already writing.
Anything that "looks like an assignment", but isn't, is subjectively a really bad idea.
export default
is perfect IMO. It also conceptually fits better with how default exports work. (I.e., they're default exports, they're not the entirety of the exports. They don't overwrite the module instance object, in the fashion thatmodule.exports =
does.)
A potential problem with export default
is that there is very little to
signify (to humans) that we are entering an expression context. For
example, the following "looks like" a declaration with a modifier, but it's
not.
export default function x() {}
x(); // ReferenceError!
It might be better to clearly indicate the expression context by a "=" or something similar.
+1
as soon as the syntax is slightly different from NPM/AMD makes sense to have less ambiguity and a better definition.
export default looks good here too
if
function x() {}
export default x;
works thought, that ain't any different from returning function expression where you cannot reuse that function if not inside the function itself.
It does not look like a big lost but more similar to expression logic we are use to with JS ... or, is it?
Now that it's been decided that braces are not a form of destructuring and
the colon replaced with as
, what's the benefit of using braces? Why not
this previous proposal?
import foo as foofoo from "foo";
import "bar" as bar;
Juan
On Jun 3, 2013, at 1:55 PM, Juan Ignacio Dopazo <dopazo.juan at gmail.com> wrote:
Now that it's been decided that braces are not a form of destructuring and the colon replaced with
as
, what's the benefit of using braces? Why not this previous proposal?import foo as foofoo from "foo";
import "bar" as bar;
Because it's ambiguous with default import; without curly braces, a single named import, without the "as" renaming, would have indistinguishable syntax from importing the default export.
On Jun 3, 2013, at 10:29 AM, Axel Rauschmayer <axel at rauschma.de> wrote:
Bikeshedding:
Well, here comes a fun thread...
Rationale: – Reducing the grawlix factor. – Making non-default imports more convenient – which I assume will happen more often(?)
That's what this comes down to: do you expect the common case to be small modules with a main export, or large modules with many named exports. We opted for the former, since there are many prominent libraries and communities in the JS world that have gravitated towards small modules. That said, the extra burden of two curly braces is very, very small.
On Mon, Jun 3, 2013 at 1:55 PM, Juan Ignacio Dopazo <dopazo.juan at gmail.com>wrote:
Now that it's been decided that braces are not a form of destructuring and the colon replaced with
as
, what's the benefit of using braces? Why not this previous proposal?import foo as foofoo from "foo";
import "bar" as bar;
I'm predicting thousands of "why" here once it's shipped. Users will be asking -- what the heck is going on here?
IM(h)O, it should be just:
import bar;
And that's it.
Below are some "syntax of my dreams" (not gonna bikeshead though):
Basic imports:
- import foo; // imports exports of module foo and binds to foo; can be used as foo.bar();
- import "mysite.com/foo.js" as foo; // imports foo from external source, can be done only via aliasing to bind name;
- import default foo; // import the default expression from module foo.
Aliasing:
- import foo as myFoo;
- import "mysite.com/foo.js" as myFoo; // already mentioned
Import all bindings to the current ns:
- import * from foo; // can be used then as bar(); baz();
- import * from "mysite.com/foo.js; // the same
Import some of the methods:
- import bar from foo; // imports only bar method from foo (w/ potential syntax ambiguity and using {})
- import {baz, bar} from foo; // ditto, two
- import {baz: myBaz} from foo;
- import bar from "mysite.com/foo.js";
Please, please, please -- would be awesome to NOT using string names (if of course possible):
module foo {} // doesn't matter whether it's lexical or not, but foo is just a name
module foo/bar {} // namespaced name, OK, can be module 'foo/bar' {}
P.S.: Although, I don't like bikesheading threads and think it should be prepared well with arguments and all implementation details before discussions.
Dmitry
Bikeshedding:
Well, here comes a fun thread...
Bikeshedding is fun : )
FWIW, I've implemented a parser for curly-free syntax and it does introduce some minor parsing difficulties on the export side. If we had lexical modules, then this:
export module as ...
could be a valid prefix for an exported module declaration or an export set. You basically need more tokens of lookahead to disambiguate on these contextual keywords.
I'm totally happy with the current syntax, modulo some concern over the default export syntax which I mentioned upthread.
import foo from "foo";
reads to the quick eye as "import the symbol
named foo from the module named 'foo'" -- but that's not actually what
it means (wrapped curlies be damned!).
I think the syntax would read much less confusingly if we made use of a
default
keyword to signify better what's happening in this declaration...
Anything from import default foo from "foo"
to the more preferable (in
my mind) import default as foo from "foo"
It occurs to me that this is valid under the current grammar:
import { default as foo } from "foo";
export { foo as default };
We've discussed using a well-known symbol for the default export, but this simple desugaring might be another option:
import foo as "foo";
// => import { default as foo } from "foo";
export default = expr;
// => let __genident__ = expr; export { __genident__ as default };
This would provide easy access to the default export on a module instance object without having to obtain a reference to a symbol.
module Foo from "foo";
F.default();
Just throwing an idea out...
Sorry - corrections to examples:
import foo from "foo";
// => import { default as foo } from "foo";
export default = expr;
// => let __genident__ = expr; export { __genident__ as default };
module Foo from "foo";
Foo.default();
Thanks,
On Jun 5, 2013, at 11:51 AM, Kevin Smith <zenparsing at gmail.com> wrote:
It occurs to me that this is valid under the current grammar:
import { default as foo } from "foo"; export { foo as default };
We've discussed using a well-known symbol for the default export, but this simple desugaring might be another option:
import foo as "foo"; // => import { default as foo } from "foo"; export default = expr; // => let __genident__ = expr; export { __genident__ as default };
This would provide easy access to the default export on a module instance object without having to obtain a reference to a symbol.
module Foo from "foo"; F.default();
Just throwing an idea out...
Yes, this is actually the direction I've been going in my thinking, based on the critique that export default is the only export form that isn't a binding form, especially when combined with a named function literal (export default function f() { ... }
). I also like the conceptual simplicity of the default export simply being a public export named "default". Moreover, Yehuda has urged me to consider
export x = 17;
as sugar for
export let x = 17;
When you put this all together, you can actually just see export default = ...
as sugar for export let default = ...
. We can also allow the keyword default
to be used for the other exported declaration forms like export function default(...) { ... }
and export class default { ... }
. (We'll finesse the .name property of such a declared function to be taken from the module name, a la Brandon's accepted proposal for .name in ES6.) To recap, the following all become valid ways to specify the default export:
export let default = 17;
export default = 17;
export default = function f() { ... };
export default = class { ... };
export function default(...) { ... }
export class default { ... }
And you can now import both the default and non-default exports of a module in a single line:
import { default as $, ajax } from "jquery";
I really like the way this sounds, just wanted to submit my approval :D
From: David Herman [dherman at mozilla.com]
Moreover, Yehuda has urged me to consider
export x = 17;
as sugar for
export let x = 17;
I'd urge const
instead of let
, as const
discourages the footgun of action-at-a-distance mutable with
/global-like bindings that I keep talking about (e.g. at esdiscuss.org/topic/importandaliasingbindings). Ideally this would extend to function
and class
as well. The desugarings then become
export x = 17;
export function x() { };
export class x { };
to
export const x = 17;
export const x = function x() { };
export const x = class x { };
In fact, if I had my way I'd continue down this path trying to eliminate mutable action-at-a-distance bindings entirely by removing export let
, export var
, and export const
. That just leaves let x; export { x };
as dangerous, and I think there are solutions to that but I won't bore you with them unless people are enthusiastic about this idea...
This makes a lot of sense. It would obviate the need for braces, right?
On Jun 5, 2013, at 3:59 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
This makes a lot of sense. It would obviate the need for braces, right?
No, at least not for imports. This is only about exports. I'd argue we should keep the braces on export { x, y, z } for symmetry with imports.
On Jun 5, 2013, at 2:58 PM, Domenic Denicola <domenic at domenicdenicola.com> wrote:
From: David Herman [dherman at mozilla.com]
Moreover, Yehuda has urged me to consider
export x = 17;
as sugar for
export let x = 17;
I'd urge
const
instead oflet
, asconst
discourages the footgun of action-at-a-distance mutablewith
/global-like bindings that I keep talking about (e.g. at esdiscuss.org/topic/importandaliasingbindings). Ideally this would extend tofunction
andclass
as well.
This is too bondage-and-discipline for JS. If you simply don't multiply-assign to the bindings they will be immutable anyway.
In fact, if I had my way I'd continue down this path trying to eliminate mutable action-at-a-distance bindings entirely by removing
export let
,export var
, andexport const
. That just leaveslet x; export { x };
as dangerous, and I think there are solutions to that but I won't bore you with them unless people are enthusiastic about this idea...
I'm not. :) I don't agree that the ability for a module to choose to export a mutable binding is bad.
On Jun 6, 2013, at 19:42 , David Herman <dherman at mozilla.com> wrote:
On Jun 5, 2013, at 3:59 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
This makes a lot of sense. It would obviate the need for braces, right?
No, at least not for imports. This is only about exports. I'd argue we should keep the braces on export { x, y, z } for symmetry with imports.
OK, so you don’t want to replace
import foo from "single_export_module";
with (braceless!)
import default as foo from "single_export_module";
On Jun 6, 2013, at 11:05 AM, Axel Rauschmayer <axel at rauschma.de> wrote:
OK, so you don’t want to replace
import foo from "single_export_module";
with (braceless!)
import default as foo from "single_export_module";
That's correct, I don't want to use import default
.
export let default = 17; export default = 17; export default = function f() { ... }; export default = class { ... }; export function default(...) { ... } export class default { ... }
Interesting, but whether we go the other forms or not, I'm sold on this simple desugaring of defaults. Looking good! : )
Bikeshedding: I generally like the module syntax, but I’d always omit the braces and use
import default
for importing the default export of a module.Rationale: – Reducing the grawlix factor. – Making non-default imports more convenient – which I assume will happen more often(?)