-
-
Notifications
You must be signed in to change notification settings - Fork 16
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
Please re-consider the "recursive unwrapping" design aspect #6
Comments
Sure! this was my first approach when trying to polyfill the idea into actual JS. I'm also afraid that we change to Something like |
Also implementing the recursive part could easily be a later standard which would hopefully get this accepted and over the line sooner? |
Recursive unwrapping makes this feature non-composable and not usable in a generic context. Consider looking up a user in a database. I may have a
(Re: @JAForbes's point. This illustrates why we could never add recursive handling afterward.) |
Either changing from recursive to non-recursive, or vice versa, would certainly be an intolerable hard breaking change. However, I think @JAForbes meant that we could add one of these later, as an additional capability, not that we'd make a breaking change. |
Ah, understood. Thank you for the clarification!
|
As I understand it, the motivation for recursive unwrapping is to make async function getPromise() {
return 1;
}
const [error, data] ?= await getPromise();
console.log(data); // 1
const [error, promise] ?= getPromise();
const data = await promise; // plain old = here
console.log(data); // [null, 1] By separating the function call from the |
The proposal seems focused on promises, but I don't understand how it can work with functions that return functions (any kind of function). As written, it seems like you can't call a function that returns a function without the returned function being called automatically (and so on until there's a result that's not a function). That would be surprising and unwanted behaviour. |
I agree with this and it was only my first idea around how to support |
I'm aware that the recursive unwrapping is a convenience, and mirrors the same behavior in promises. However, I would like to urge its reconsideration -- or at the very least, register the objection here in an issue, for posterity sake.
Back Story: Promises
The fact that I cannot carry a Promise inside another Promise (
Promise<Promise>
) creates an overfit in locality of result handling. By that I mean, if Iawait
orthen()
a promise, I'm forced to deal with the ultimate result of that at that point in the code. I cannot hold multiple layers of result wrapped together, and progressively unwrap and handle those layers one at a time across a call-stack.To put this in other terms, the recursive unwrapping (or refusal to even nest!) design aspect of promises is what makes
Promise
not compatible with the monadic laws -- despite all the well-intentioned blog posts that claim JS promise is a monad. This design choice is what breaks down a variety of techniques that are established around those guarantees (in the world of FP programming).Undoubtedly, convenience is a strong driver for that design. Probably also performance.
But I think the case could/should still have been considered that, it would have been preferable if there was an option to nest Promises, and to unwrap them one level at a time, even if the default behavior might have been to recursively flatten them out.
Here We Go Again
So now we arrive at the present
?=
proposal, and the implied[error, data]
tuple type. If we handwave a bit, this type is basically an Either monad. Which is cool.The explainer illustrates we can manually construct nested tuples, just like you could do with real Either instances. That's cool, and an improvement over the Promise type which didn't allow the nesting at all (it basically unwraps/flattens at
resolve()
time). I assume that if the tuple in question were an actual concrete value type of the language (asPromise
is), the design would probably prevent such nesting.Since we can nest these values, we're closer to being able to take advantage of the relevant monadic guarantees and design patterns. But then the
?=
recursive unwrapping kicks the legs out from underneath us. If we use?=
, we lose the monadic'ness. We can make Either piggyback on the tuple type's design, but have to use custom userland functions for the handling, instead of the code readability and attractiveness of?=
operator itself. That's disappointing.Reconsider?
I'm not asking to discard the recursive unwrapping entirely, but rather, could we possibly have both options available, one where the recursive unwrapping is done (for those who prefer the convenience), and one where the unwrap is not recursive?
I could bikeshed here on
?=
vs?*=
as a pair of operators for this purpose. But before that's relevant to discuss, I just wanted to raise the question if we could reconsider this design decision before it's too far baked?The text was updated successfully, but these errors were encountered: