Mle
Mle
CC#
Created by Kiel on 10/23/2023 in #help
❔ Reducing Result-pattern annoyances
This is a very simple case, in which only the first 'step' of the algorithm you're executing may fail. You can think of some other common cases and extract their logic to methods on the base Result type. For example. What if the second step may also fail and you don't want to endlessly wrap Results inside Results? What if you want the algorithm to continue even if one of the steps failed while aggregating all the errors in the process? What if some steps of the algorithm might need to execute asynchronously? What if you're not mapping the value wrapped in the result but instead doing something with it (like saving to a database?) What if you have a list of results and want to turn it into a Result of a list? Think about what's practical for you and what's not and try to extract the common logic to the Result class, it's not as anemic as it seems at first.
9 replies
CC#
Created by Kiel on 10/23/2023 in #help
❔ Reducing Result-pattern annoyances
Let's start with some basic operations that you can try defining on this. Let's call the first one Map or you may know it better as Select (from Linq). It's purpose is to replace the value that can potentially be wrapped in the result or do nothing if the result is failed. This is more like pseudocode rather than something that will actually compile, but try to understand the concept.
public Result<TMapped> Map(Func<T, TMapped> mapper)
{
if (IsSuccess)
return Ok(mapper(Value));

return Error(ErrorMessage);
}
public Result<TMapped> Map(Func<T, TMapped> mapper)
{
if (IsSuccess)
return Ok(mapper(Value));

return Error(ErrorMessage);
}
And a usage of it would be similar to this.
async Task<Result<Transformed>> DoSomething()
{
Result<Original> firstStepResult = await service.TryFirstStepAsync();
return firstStepResult
.Map(v => {
return Transform(v);
});
}
async Task<Result<Transformed>> DoSomething()
{
Result<Original> firstStepResult = await service.TryFirstStepAsync();
return firstStepResult
.Map(v => {
return Transform(v);
});
}
Note that Transform will be called only if the first step was successful, otherwise the original error message would be propagated to the final result.
9 replies
CC#
Created by Kiel on 10/23/2023 in #help
❔ Reducing Result-pattern annoyances
public sealed class Result<T>
{
public string? ErrorMessage { get; }
public T? Value { get; }
public bool IsSuccess => Value is not null;
public bool IsError => !IsSuccess;
}
public sealed class Result<T>
{
public string? ErrorMessage { get; }
public T? Value { get; }
public bool IsSuccess => Value is not null;
public bool IsError => !IsSuccess;
}
I'm assuming you have something like this in your code, alongside some ctors or static factory methods to initalize instances of Result
9 replies
CC#
Created by Kiel on 10/23/2023 in #help
❔ Reducing Result-pattern annoyances
Hey Kiel, good question. Preferably you should provide the code that you have in the Result<T type that you have, but just from the info provided in your message, I might have some suggestions. You can define some commonly-used operations as higher order functions on the original result type. Don't be scared of the term higher order function, it's just a fancy way of saying a function that either accepts or returns a callback. (think System.Funcs, System.Actions of .NET).
9 replies