FluentResults icon indicating copy to clipboard operation
FluentResults copied to clipboard

Add Extension methods "Bind" and "Map" for IResult<T>

Open DrPepperBianco opened this issue 1 year ago • 0 comments

I tried to change my code from Result<TValue> to IResult<TValue> and realized, that IResult<TValue> doesn’t have a Bind or a Map methods.

I think Bind and Map are very important features, therefore I propose adding those methods as Extensions methods.

I prepared some code changes in a fork of this project (see here: https://github.com/DrPepperBianco/FluentResults/blob/features/extensionmethods_for_interface_types/src/FluentResults/Extensions/ResultExtensions.cs).

What I did is, I added extensions methods for IResult<TValue> and also for IResultBase for the following methods:

  • MapErrors
  • MapSuccesses
  • Bind
  • Map
  • ToResult<TValue>

I copied those methods from the Result class and the Result<TValue> class.

I designed those methods, to return either IResult<TValue> (Task<IResult<TValue>>, ValueTask<IResult<TValue>> resp.) or IResultBase (Task<IResultBase>, ValueTask<IResultBase> resp.).

Whenever a new result is created, the IResult<TValue> is actually a Result<TValue>.

This has the disadvantage, that, if the input result had covariance, the output result loses this covariance. This could be circumvented with Reflection, but I am not sure, if this is a good idea.

Alternatives:

Instead of directly providing extension methods for Map, Bind, etc., we could just provide an extensionsMethod AsResult() like that:

public static Result<TValue> AsResult<TValue>(this IResult<TValue> result) 
{
    if(result is Result<TValue> _result)
    {
        return _result;
    }
    else
    {
        return new Result<TValue>()
            .WithValue(result.ValueOrDefault)
            .WithReasons(result.Reasons):
    }
}

Then you could convert an IResult to a result, before working with it.

This method assumes, that most IResults are actually already Results. That is of course not the case, if the result is covariant. In that case, we create a new result object. I named the method AsResult, because in most cases it doesn't create a new object. But that is of course not true, the better name would be ToResult, but that name is of cource already taken for another functionality.

The advantage is, we have fewer methods to maintain. The disadvantage is, that in the case of actual covariance we have create a new object, while an actual extensions method for MapErrorss for instance, could just return the input object instead.

So what do you think?

DrPepperBianco avatar Aug 23 '23 10:08 DrPepperBianco