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

[Feature request] have .unwrapOr() equivalent that runs a function #587

Closed
janglad opened this issue Sep 27, 2024 · 10 comments
Closed

[Feature request] have .unwrapOr() equivalent that runs a function #587

janglad opened this issue Sep 27, 2024 · 10 comments

Comments

@janglad
Copy link

janglad commented Sep 27, 2024

Title, probably best to implement it as a new method as this could be breaking for people that are currently returning functions from .unwrapOr().

Example usecase specific usecase for me

declare const getSignedInUser: () => ResultAsync<User, AuthError> 

import { redirect } from "next/navigation";
const redirectToAuth = () => {
 // Throws under the hood so has returntype of never
    redirect("/login")
}

// On page
const user = await getSignedInUser().unwrapOr(redirectToAuth)

but a more common use case would be having a dynamic value as a fallback (result.error could be provided as argument) or one that you want to compute lazily.

If this is something other people want please leave a suggestion for naming and I can probably implement this.

@paduc
Copy link
Contributor

paduc commented Sep 27, 2024

Hello @janglad

I believe match can handle your use case.

const user = await getSignedInUser().match( (user) => user, (e) => { redirectToAuth() }); 

@mattpocock
Copy link
Contributor

@janglad mapErr does this, I think

@janglad
Copy link
Author

janglad commented Sep 28, 2024

@mattpocock not quite, still leaves you with a Result

interface User {
  id: string;
}

interface AuthError {
  code: string;
}

declare const getUser: () => ResultAsync<User, AuthError>;
declare const redirect: () => never;

const user = await getUser().mapErr(redirect);
//  Result<User,never>

const user2 = await getUser().match((user) => user, redirect);
//    User

while not super on topic for this issue this does make me wonder if a Result<T, never> should be typed as an Ok<T, never>? The Ok would have .value available right away without checking for isOk(), and the never already indicates that there can't be any error case. Altho no idea if this is actually implemented differently and might cause issues.

@paduc that's true, but while obv a quick val => val isn't the biggest deal it's also not the most elegant.

@paduc
Copy link
Contributor

paduc commented Sep 28, 2024

@janglad I believe what you are trying to do may be outside the scope of this lib, which is handling error types. You are trying to add behavior / actions.

There are plenty of ways to achieve this without adding to the API.

@janglad
Copy link
Author

janglad commented Sep 28, 2024

@paduc yea agree that redirecting is def out of scope, but not sure if using a function to get the fallback is? Similar to how on Java optionals you have orElse(val) and orElseGet(() -> val). But I guess that it's not a very common use-case seeing how it hasn't come up before.

@iyefrat
Copy link

iyefrat commented Oct 6, 2024

I'm not sure if emulating Rust's result module in it's entirety is a goal here, but if so, they have a unwrapOrElse method that does exactly this https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap_or_else

@supermacro
Copy link
Owner

I'm not sure if emulating Rust's result module in it's entirety is a goal here

Correct.

https://gdelgado.ca/neverthrow-subtyping#title


Also why does match not meet your needs?

@iyefrat
Copy link

iyefrat commented Oct 30, 2024

Also why does match not meet your needs?

unwrapOr is also redundant with match`, it's just a clearer declaration of intent I think:

result.match((x) => x, () => 'an error')
result.unwrapOr('an error')
// similarly
result.match((x) => x, (error) => JSON.stringify(error))
result.unwrapOrElse((error) => JSON.stringify(error))

@paduc
Copy link
Contributor

paduc commented Oct 30, 2024

@iyefrat I agree that unwrapOr is also syntactic sugar but just a nitpick: it is used to pass a default value not an error.

@iyefrat
Copy link

iyefrat commented Oct 30, 2024

oh sure, I just didn't really have a default value to use as a generic example, but you're right that a better sketch would be:

result.match((x) => x, () => 'default')
result.unwrapOr('default')
// similarly
result.match((x) => x, (error) => recoverFromError(error))
result.unwrapOrElse((error) => recoverFromError(error))

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

5 participants