vkhorikov/CSharpFunctionalExtensions

[Suggestion] `Result.Of()` to start a chain with a value

Closed this issue ยท 3 comments

In our code we often want to start a chain with a value.

In the following examples, we use the _service.CreateSomething() method, that always returns a non-nullable value.

There are several ways to start the chain:

We could start with the Result.Success() method.

Result.Success(_service.CreateSomething())
  .Tap(something=> ...)

But this won't work if the value is async. Since there is no async overload for the Success() method we could use a combination of Success() and Map().

await Result.Success()
  .Map(() => _service.CreateSomethingAsync())
  .Tap(something=> ...)

This creates some unnecessary overhead because we have to start with an empty Success Result first and has caused confusion to some new CSharpFunctionalExtension users.

Another way would be to use Maybe.From() as the entry point:

Maybe.From(_service.CreateSomething())
  .ToResult("Unecessary error message")

The downside is that we have to provide an error message even though there'll never be an error because the operation never returns null. In addition, there is no async overload.


Our suggestion is to add a new static method Result.Of() that always returns a Success Result<T> with the provided value. This has a better semantic meaning that the alternatives shown above. There is also an async variant where you can pass a Task<T> so you can always start the chain in the same way.

So based on the examples above you could do:

Result.Of(_service.CreateSomething())
  .Tap(something=> ...)
await Result.Of(_service.CreateSomethingAsync())
  .Tap(something=> ...)

We could also add overloads to pass Funcs which adds some flexibility.

Result.Of(() => _service.CreateSomething())
  .Tap(something=> ...)

For reference typescript-functional-extensions has a .from() method to return a Result<T> from async funcs. But to make it more distinguishable from the Maybe.From() method we thought that Result.Of() might be more fitting than Result.From().

The implementation could look like this:

public static Result<T> Of<T>(T value) where T : notnull
{
    return Result.Success(value);
}
    
public static async Task<Result<T>> Of<T>(Task<T> valueTask) where T : notnull
{
    var value = await valueTask;

    return Result.Success(await value);
}

We could add the type constraint notnull so that there is definitely a value and therefore a Success Result. If the value might be null it would indeed be better to use Maybe.From().


What do you think about this?

If you don't want to add a new factory method we could also add an async overload to Result.Success(). That would already help. But I think Result.Of() is semantically nicer.

I would appreciate that. Thumbs up!

Result.Of sounds good. Feel free to submit a PR.

@vkhorikov cool, I'll be working on a PR sometime in the next weeks.