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

Add Fold function to Result monad #42

Merged
merged 7 commits into from
Jun 22, 2024
Merged

Conversation

taman9333
Copy link
Contributor

@taman9333 taman9333 commented Jun 5, 2024

This pull request introduces Fold function to the Result monad, enhancing its functionality and making it more versatile for handling both success and failure scenarios.

Changes Made:

  • Implemented a Fold function within the Result monad, allowing for streamlined handling of success and failure cases.
  • The Fold function takes two callback functions as arguments:
    • successFunc: A function to be applied if the Result contains a successful value. It transforms the value into another type.
    • failureFunc: A function to be applied if the Result contains an error. It transforms the error into another type.
  • If the Result contains an error, the Fold function applies the failureFunc to the error and returns the transformed result.
  • If the Result contains a successful value, the Fold function applies the successFunc to the value and returns the transformed result.

Benefits:

  • Provides a clean and declarative way to handle both success and failure cases within the Result monad.
  • Increases the versatility and usability of the Result monad by enabling developers to easily define custom behavior for different scenarios.
  • Enhances code readability and maintainability by encapsulating error-handling logic within the Result monad itself.
  • Enables translation between different monad types, the Foldable[T, U] type can be folded into a new type R

Example:

func main() {
	result := maySucceed(5)

	foldResult := result.Fold(onSuccess, onFailure)
	fmt.Println(foldResult)
}

func maySucceed(value int) mo.Result[int] {
	if value < 0 {
		return mo.Err[int](errors.New("Value must be greater than 0"))
	}

	return mo.Ok(value)
}

func onSuccess(value int) interface{} {
	return fmt.Sprintf("Success: %v", value)
}

func onFailure(err error) interface{} {
	return fmt.Sprintf("Failure: %v", err)
}

@taman9333
Copy link
Contributor Author

@samber

@samber
Copy link
Owner

samber commented Jun 17, 2024

I would use the same prototype as other methods: Fold(onSuccess[T], onFailure[T]) T

Feel free to add this method to the other types. Or I will do it by myself.

@taman9333
Copy link
Contributor Author

@samber Okay I think you are asking to have something like that

func (r Result[T]) Fold(successFunc func(T) T, failureFunc func(error) T) T {
	if r.err != nil {
		return failureFunc(r.err)
	}
	return successFunc(r.value)
}

However, I was thinking that the Fold function could map the result to a new type. This would allow the client (AKA the caller) to use custom functions that return any value based on their specific logic needs. For example, if the result monad encounters an error, instead of returning a fallback integer value, I would like to return a string containing an error message. So what do you think about that

@samber
Copy link
Owner

samber commented Jun 17, 2024

To date, Go does not allow type parameters on methods: golang/go#49085 .

I don't really like returning interfaces, since it is not very typesafe.

In your case, I would suggest an additional helper: func Fold[T any, U any, R any](Foldable[T, U], func(T) R, func(U) R) R

IMO Option, Result and Either should implement the following interface:

type foldable interface[T, U] {
   left() T
   right() U
}

@taman9333
Copy link
Contributor Author

@samber Here are the changes. Are you okay with these changes before implementing the Foldable interface in the remaining types (Either & Option)?

Also, I have a question: I've noticed different implementations of the fold function in various languages/packages. For instance, for the Result<TValue, TError> monad, the fold function always returns a value of type TValue, as you mentioned here. However, in other packages, I see that the fold function returns a new type.

So, I'm unsure which approach makes more sense. Should we make the fold function consistently return the same type TValue? If so, would it be acceptable to implement another function that returns a new type?"

@samber
Copy link
Owner

samber commented Jun 19, 2024

Looks good.

I think we can return a different type in the case of mo.Fold, but it must be the same type if you also implement option.Fold

@@ -15,6 +15,6 @@ jobs:
stable: false
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v5
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update to newer version as I got the following linting error

func Result[T].{{func_name}} is unused (unused)

regarding those functions

@taman9333
Copy link
Contributor Author

@samber I have changed method names for Foldable interface to make sure method names will not conflict with field names in the Either struct. Are you okay with that 👀

@samber
Copy link
Owner

samber commented Jun 22, 2024

Looks good!

Let's merge ;)

@samber samber merged commit ef47e28 into samber:master Jun 22, 2024
1 check failed
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

Successfully merging this pull request may close these issues.

2 participants