09:22
<pokute>
I've been dogfooding my TypeScript support for pipeline operator at https://github.com/Pokute/AoC2021/blob/main/4.ts . It's starting to feel crucial for me. Lacking tacit function application (|>>) would be an inconvenience, but the other way around, I would have so many IIAFEs.
20:28
<bakkot>

A proposal: https://gist.github.com/bakkot/3d0f81233fc00b508ae5f247b1458823

tl;dr: adding syntax for defining a function which can be either sync or async, depending on how it's called:

async? function f(possiblyAsyncCallback) {
  let x = await? possiblyAsyncCallback();
  return something(x);
}

console.log(f.sync(syncCallback)) // a regular value

console.log(f.async(asyncCallback)) // a Promise
20:28
<bakkot>
looking for any feedback on whether this seems at all reasonable before I put together something to present to committee
20:46
<Justin Ridgewell>

A proposal: https://gist.github.com/bakkot/3d0f81233fc00b508ae5f247b1458823

tl;dr: adding syntax for defining a function which can be either sync or async, depending on how it's called:

async? function f(possiblyAsyncCallback) {
  let x = await? possiblyAsyncCallback();
  return something(x);
}

console.log(f.sync(syncCallback)) // a regular value

console.log(f.async(asyncCallback)) // a Promise
Have you seen gensync before?
20:48
<bakkot>
I had not!
20:49
<bakkot>
but that is basically exactly the same thing, neat
20:55
<loganfsmyth>
Let me know if you have suggestions for improvements, I think babel is the only thing really using it right now.
21:08
<bakkot>
main thing which looks to be missing to me is a way for the function to switch on whether it was called as sync or async
21:08
<bakkot>
so that it can e.g. call the appropriate sync or async version of some other API
21:09
<bakkot>
(my gist has a function.async meta-property for this; it would be a bit harder to do in a library)
21:14
<loganfsmyth>
Got it, should be pretty easy for you to make a helper to do that since you can make a function where the async version returns true and the sync version returns false and then do if (yield* isAsync()) {
22:28
<nicolo-ribaudo>
Got it, should be pretty easy for you to make a helper to do that since you can make a function where the async version returns true and the sync version returns false and then do if (yield* isAsync()) {
Yup, Babel already has it: https://github.com/babel/babel/blob/2a3b0b96012b86c558aec344dad34a60c51a71c9/packages/babel-core/src/gensync-utils/async.ts#L23
22:31
<loganfsmyth>
Hah I though it did but I was on mobile and couldn't be bothered to look
22:32
<loganfsmyth>
Certainly something we could move into gensync too
22:34
<nicolo-ribaudo>
Btw, something that would greatly benefit from moving this to the language (rather than as a library) are stack traces and step-by-step debugging; gensync makes it really hard (this is not a critique, just a limitation I don't think can be solved in a library).
22:53
<bakkot>
I think the main annoying thing with the library version is that you can't call regular async functions without wrapping them first (unless I'm missing something)
22:53
<bakkot>
not a huge hinderance but would be nicer not to need to worry about it
22:54
<bakkot>
anyway, this is really cool; I will play with it some
22:55
<nicolo-ribaudo>
Well, it's await? (function.async ? asyncFn() : syncFn()) vs yield* gensync({ sync: syncFn, async: asyngFn })() (you need the check in both versions)
22:55
<nicolo-ribaudo>
But yes, you always need to wrap
22:56
<bakkot>
with the syntax you need to wrap if you're calling a function you figured out yourself, but not if you're calling a function the user provided
22:57
<bakkot>
i.e. you can just do await? callback() and if the user called you as f.async and passed an async callback, or if the user called you as f.sync and passed a sync callback, it will work the same
22:57
<bakkot>
and callback doesn't need to be wrapped
22:58
<nicolo-ribaudo>
Oh ok yes, we had to introduce a maybeAsync gensync helper in Babel for that
22:58
<nicolo-ribaudo>
(which also throws if callback() returns a promise when called in a sync context)
22:59
<bakkot>
yeah there's definitely some possibility of shooting yourself in the foot here, if you mess up what's async and what's sync
23:00
<bakkot>
this would be fun for typescript to figure out :P