Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shorthand for module blocks default-exporting functions #6

Closed
littledan opened this issue Nov 12, 2020 · 16 comments
Closed

Shorthand for module blocks default-exporting functions #6

littledan opened this issue Nov 12, 2020 · 16 comments

Comments

@littledan
Copy link
Member

Module blocks are intended to make it more ergonomic to write context-free fragments of code, but there's still a bit of ceremony around making them a module. Maybe we could reduce this with a small amount of syntax sugar: enabling the module keyword to precede a function expression, to make it a module which default-exports that function. For example:

fn(module function(x) { return x; })

would desugar to

fn(module { export default function(x) { return x; } })

I think we could enable async functions, arrow functions, generator functions, and functions with local name bindings the same way, even permitting function declarations (to set the external binding to the module) if desired. For each of these, there is some careful work to do to spell out the grammar details, make sure we're all OK with the edge cases (e.g., [no LineTerminator here]) and that parsing isn't too costly (e.g., for arrow functions).

@surma
Copy link
Member

surma commented Nov 12, 2020

I like this, but I wonder if we should see what kind of patterns emerge? Considering you can’t close over variables, I am not sure if 1-function-modules would actually appear that often. Haven’t thought about it that much tho

@littledan
Copy link
Member Author

I wonder if we should see what kind of patterns emerge?

This sounds like a good idea to me; we can categorize this as a potential follow-on proposal.

@domenic
Copy link
Member

domenic commented Nov 30, 2020

The discussion in #21 moved in this direction and IMO ups the priority of this considerably, for the reasons explained there. I'd also suggest that further shorthand would be ideal, e.g. module (x) => { return x; }.

@nicolo-ribaudo
Copy link
Member

I think that "module arrow functions" would be really confusing. The difference between arrow functions and "normal functions" is that they capture this and arguments from the outer scope, and this wouldn't be possible with module functions.

@domenic
Copy link
Member

domenic commented Nov 30, 2020

Sure, but normal functions already capture everything else from the outer scope, and the proposal in the OP is breaking that property. I don't think adding two more bindings to that exception list is a huge loss...

@littledan
Copy link
Member Author

I like the idea of module arrow functions. We would probably want to support the full set of variants of function expressions, including generators, async functions, etc in "module" form. I am also sympathetic to arguments to make this shorthand post-MVP.

@surma
Copy link
Member

surma commented Dec 1, 2020

I definitely agree with @domenic that these are desirable ergonomics, especially as libraries like greenlet have already somewhat established those ergonomics. I do feel like it’s something I’d rather tackle as an enhancement post-MVP. The benefits of module { export default (x) => { ... } } vs module (x) => { ... } seem desirable but non-essential to me.

I also wonder how often a single-function module will actually be used frequently. I could imagine that many use-cases require imports, which would require the full module syntax (unless you want to dynamically import them). I’d like to see module blocks in use for a bit before deciding on this.

@littledan
Copy link
Member Author

What I think we can do is make a separate Stage 1 proposal for this shorthand, with a concrete grammar and specification, and encourage its implementation in Babel.

@littledan
Copy link
Member Author

littledan commented Dec 7, 2020

I could imagine that many use-cases require imports, which would require the full module syntax (unless you want to dynamically import them).

Thinking about this some more, I agree with @surma that it's not yet clear that import-free single-function modules are needed so frequently. You will probably usually want to import something, and dynamic import is just not an idiom that I want to encourage to get around this. That makes me see this as more of a post-MVP feature.

@littledan
Copy link
Member Author

@ByteEater-pl
Copy link

The discussion in #21 moved in this direction and IMO ups the priority of this considerably, for the reasons explained there. I'd also suggest that further shorthand would be ideal, e.g. module (x) => { return x; }.

or even just module (x) { /* body */ } – seems syntactically unambiguous

@acutmore
Copy link

The FAQ in the README suggests that the short hand for a module block isn't part of the MVP

For now we plan to leave the MVP without such a shorthand and observe what kind of usage patterns emerge. If import-free single-function modules become a frequently recurring pattern, a module shorthand seems like a good idea to add.

But the README also talks about adding syntax for it here: https://github.com/tc39/proposal-js-module-blocks#module-function-syntactic-sugar

@surma
Copy link
Member

surma commented Oct 25, 2021

That’s an oversight by me. We went back and forth and, as of now, the shorthand is part of the proposal.

@kriskowal
Copy link
Member

I concur with @littledan that it would be good to defer module default export shorthand(s) to a future proposal.

@hax
Copy link
Member

hax commented Oct 26, 2021

@surma I suggest we could reopen this issue (or create a new one?), and I also agree with @kriskowal that we'd better defer module function to a future proposal.

@Jack-Works raised a concern about the syntax of it in yesterday meeting.

module function (a = expr) { ... }

expr is also in the enclosed scope of the module block, but is out of { ... }, this could be a little bit confusing because most developers understand {} as the scope indicator. (Though we could educate them that module keyword is the indicator, not {}, as shu said in the meeting.)


In the yesterday JSCIG meeting (a localized meeting of chinese TC39 delegates and the chinese JS community) some also raised another concern: if we have module function, for language completeness, it should also include other similar things, eg. arrow function (like module () => ...) or hack style function (like module +> ^ +1), or class (module class { ... }) or even a sugar for general expression (maybe like module do { ... } ). (The interesting thing is when I write this comment I find that the latter three do not have the scope confusion of default parameter because they do not have parameters 😅 )


About this confusion @nicolo-ribaudo mentioned, I agree it's a problem, but this is because of the nature of this semantic in module + module block isolating, so I feel it's just a special case of scope confusion, IMO it won't be worse than the default parameter scope confusion.

@surma
Copy link
Member

surma commented Oct 26, 2021

I’ll open a new issue soon to talk about the concrete shorthand proposal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants