Open benjamingr opened 9 years ago
we should discuss this some as some things become confusing like async cached bar()
would be both using the async composer and the cached composer?
@bmeck not to mention async cached
and cached async
would mean different things. It would definitely be an interesting issue to discuss how compositional functions... compose.
As for the question of why we don't just do this with yield, the committee has already decided that the await syntax is desirable enough to pursue because of the status of async/await. This proposal simply aims to make that composition mechanism generic.
Although composition functions are flexible enough to be used for any type of decoration, the intention is that they used to compose different scalar asynchronous primitives.
My personal opinion is that if you want decoration on any function, we should do that within the context of the decorator proposal. Composition functions are different in that they activate the await syntax, which does not appear to be necessary for many of the examples above. I'm a big fan of decorators and would like to see them used for the use cases you outlined.
@jhusain wouldn't it be preferable to kill the two birds in one stone?
Like you said, these things are powerful enough for it. The syntax they give works and is frankly really nice for decorators - it's clean and unambiguous.
Why do we need to make a special case of decorators that don't use the await
hook?
I think this might also make them a lot easier to sell to people who don't really understand the need for asynchronous primitives at all.
It's definitely an option. I'm not sure how the committee will react to Allowing N modifiers on a function, and exactly what the implications on the parser would be. The most obvious downside would be allowing await in functions where it was not valid.
I'm not sure I understand, doing async function foo(){}
return a function already, JS has function expressions, and unlike generators where the *
is part of the function async
prefixes the function.
What would prevent me from doing
async async function foo(){}
In the current async functions proposal today? It compiles just fine with Babel, it's pointless but it's valid syntactically.
@jhusain the grammar would get to be in a very bad state, you would need to have the modifiers after the parameter list to avoid confusion like dart does function foo() async cached {}
@bmeck I agree that this poses a problem and complicates the syntax, I'm not sure if it does so in an ambiguous way - which is why in my original post and examples I used compose(autocurry, cached)
and not autocurry cached
.
Perhaps someone with more parser experience like @sebmck could shed some light on the complexity (if at all and how much) involved in something like this and why async async
works at an engine level.
@benjamingr right now it works because async is a well known token that is placed in https://github.com/babel/babel/blob/0112c6377914fcb5a1d0287b0bbc5c986d83c6fe/src/babel/transformation/helpers/remap-async-to-generator.js once it becomes a generic identifier things start to get stranger because it requires lookahead.
@bmeck thanks, that definitely explains it. How would syntax resolution look at this proposal? Does this mean you can only create a compositional function when it's created? (So you can't make a function async from the outside). To be admit, I can't see compelling use cases to make a function "async from the outside" (it also has issues since await
would not be well defined if we don't know in advance if it's a compositional function or not)
I posted an issue on the decorators proposal, I think that if decorators are given a hook to await
we pretty much get this very proposal only with different syntax. I wonder if it can't be solved in all three places at once.
@benjamingr as it stands async is not done at runtime, it is done at compile time which is partially reflected by the syntax, decorators proposal is done at runtime. The syntax most likely would want to change for await if we had runtime decoration be possible, however async/await
introduce a new keyword inside the scope so they should not be decorated at runtime. Instead use a function wrapper (or the decorator proposal) to wrap them:
let foo = @cached async function() {
}
This is all due to compositional/async functions introducing new syntax in the function. We don't allow function decorators to introduce new syntax and probably should not.
TL;DR - compositional changes the yield
keyword to await
which makes it unable to be combined fully w/ function decorators.
@benjamingr on a different note regarding making async into a decorator by itself, this is possible if we used yield
as the key word instead of await
. I would generally even prefer this on a personal style level.
let foo = @cached @async function* () {
}
is fairly easy to parse / does not require lookahead.
let foo = cached async function() {
}
would require lookahead after lexing async
to see if the tokens eventually form a function after the list of identifiers.
Yes, I see that now. Wouldn't it be possible to require that decorated functions reserve await
as a keyword? It'd mean it's more work to desugar but it also means this whole thing can be done in the userland (I definitely think the language can, and should provide @async or async) with a hook.
This would also have the same advantage as this proposal of allowing us to use userland implementations and prototype awaiting different things (like cancellable promises for example).
That said, in its own async/await
is an enormous improvement for the masses in writing async JavaScript, it's a bit of a shame you're stuck with the engine's and language's implementation and idea of what primitives you must use but it's possible to yield observables this way (by a then
, or a proposed symbol) and it's still a big improvement over the status quo.
reserving keywords is pretty cheap for compilers, but I would want to ask people about the goals for language macros (was planned for es8 / es2017) before saying anything about reserving more keywords.
I think it would be really beneficial in this proposal to explain what this does that
yield
does not enable you to do at the moment. As is, why would you want compositional functions given you can already do this in not-so-bad syntax with yield.I think it might also be very beneficial to expand this proposal (without changing semantics) and explain how this can be used for arbitrary decorators.
Here are some interesting use cases that can be implemented with compositional functions and enjoy this syntax.
I think the fact we can get arbitrary decoration in class declarations and expressions should not be understated here and that there are a lot of interesting use cases to show.
Also, I wonder why not add
async
as a global which makes this proposal completely possible on top of the async/await one and can streamline it.