CSharpFunctionalExtensions icon indicating copy to clipboard operation
CSharpFunctionalExtensions copied to clipboard

Result to Maybe

Open Uli-Armbruster opened this issue 4 years ago • 8 comments

Hi Vladimir, thanks for that great library and your premium stuff on Pluralsight. I was wondering why there is no Extension on Result<T> to convert it to a Maybe<T> like the following:

public static class ResultToMaybeExtensions { public static Maybe<T> ToMaybe<T>(this Result<T> result) { return result.IsFailure ? Maybe<T>.None : Maybe.From(result.Value); } }

Do I misunderstande these two concepts so that this is not a valid use case? For example I would like to offer the value object Address on an Entity Customer as a get-only Maybe. Therefore I would use Address.Create(...) and later in the process I need to transform it to a Maybe monade.

Uli-Armbruster avatar Oct 22 '21 13:10 Uli-Armbruster

That's a good question. I don't think it's a good idea to have such an extension method because it leads to the loss of information during the conversion -- the error that caused the failure is now lost. Generally, you want your code to explicitly check for errors in the result, you don't want it to silently convert to a Maybe.

If you do want to omit the information about the error, then you can use GetValueOrDefault. This method acts similarly to ToMaybe and you are being explicit that do don't need to error info. This code should compile as-is (though I didn't check):

Result<User> result = ParseUser();
Maybe<User> user = result.GetValueOrDefault();

vkhorikov avatar Oct 30 '21 10:10 vkhorikov

Okay, so I use this way. The scenario was the following: You have an Entity and you perform a business action. Within this method we try to create a Value Object. If the creation produces errors, the business action returns an error result. If not, it returns success. But: The Entity has a property of that Value Object, e.g. Address. And I want to tell the consumers: Hey, Address is maybe not there. For most scenarios we built dedicated NULL-Objects, e.g. NoAddress.Instance. But with EF Core this is always annoying, so there are cases when I say: Maybe instead of an NULL-Object is a easier way.

Uli-Armbruster avatar Nov 05 '21 08:11 Uli-Armbruster

Yeah, these are two different scenarios. You are right to model the first one as a Result and the second one as Maybe. The conversion between them using the explicit result.GetValueOrDefault() seems to be the most appropriate.

vkhorikov avatar Nov 05 '21 11:11 vkhorikov

Thanks Vladimir for explanation. :+1: I would really enjoy seeing your Pluralsight course EF Core Encapsulation updated to the new possibilities of EF Core 6. :v: Maybe I can contribute to or support you with Code Samples

Uli-Armbruster avatar Nov 05 '21 12:11 Uli-Armbruster

Hey Vladimir,

as @hankovich already mentioned in #358 , the .GetValueOrDefault() method is currently available only for Maybe. Do you plan to implement this method on Result as well?

Thank you for your answer in advance.

BernhardMaier avatar Nov 15 '21 06:11 BernhardMaier

Yes, it is planned, just didn't have time yet to implement it. Feel free to submit a PR.

vkhorikov avatar Nov 16 '21 14:11 vkhorikov

I'd like to chime in that I still feel that a ToMaybe extension is still needed.

  1. The .GetValueOrDefault() is only on Result<T>, and not on Result<T,E>.
  2. It should be up to the developer to decide when they want to throw away information.
  3. .GetValueOrDefault() requires a cast which means you have to explicitly add type information. This may be fine when creating a variable (though I still would prefer var), but becomes awkward in a LINQ query.
  4. Maybe already has a LINQ extension method for Choose.
  5. Returning null causes extra warnings when nullable reference types are enabled.
public override IEnumerable<AimNumber> Shrink(AimNumber type)
{
    var aimString = AimNumber.AsString(type);
    
    return Arb.Shrink(aimString)
        .Select(AimNumber.FromString)
        // IDE complains about r being nullable and a type mismatch to the method return signature
        .Select(r => (Maybe<AimNumber>)r.GetValueOrDefault())
        .Choose();
}
return Arb.Shrink(aimString)
    .Select(AimNumber.FromString)
    .Select(r => r.ToMaybe()) // No IDE complaints, more concise and clear
    .Choose();

BennieCopeland avatar Sep 08 '23 00:09 BennieCopeland

I agree, we can put ToMaybe in.

For reference: I've tried to follow my past reasoning, but it doesn't quite make sense to me now. What I was arguing against is implicit conversion from Result to Maybe, as in:

Result<Email> result = Email.Create(request.Email);
Maybe<Email> maybe = result; // Implicit conversion

That would be a bad idea. But with a method call (be it GetValueOrDefault or ToMaybe) we don't really do an implicit conversion, but an explicit one. Here's more on this topic: https://khorikov.org/posts/2021-11-08-converting-result-to-maybe/

Please feel free to submit a PR.

vkhorikov avatar Sep 18 '23 11:09 vkhorikov