JohannesMoersch / Functional

Functional is a set of libraries that support functional programming patterns in C#.
MIT License
66 stars 13 forks source link

Result.ZipFailure factory method #146

Open RyanMarcotte opened 1 week ago

RyanMarcotte commented 1 week ago

The Result.ZipFailure factory method is similar to Result.Zip, but for cases where the TSuccess type is identical and the TFailure type varies between results. An example scenario where this is useful is when combining multiple Result<Unit, TError> instances to produce some aggregate error object.

// assume existence of the following operations
// Result<Unit, DomainError<RetailSaleId, RetailSaleLineItemId>> RetailSaleLineItem.Delete()
// Result<Unit, DomainError<RetailSaleId, RetailSaleItemRentalId>> RetailSaleItemRental.Delete()

// produces Result<Unit, DomainError<RetailSaleId, RetailSaleLineItemId>[]>
var lineItemResult = LineItemCollection
    .Select(x => x.Delete())
    .TakeAll()
    .Map(_ => Unit.Value);

// produces Result<Unit, DomainError<RetailSaleId, RetailSaleItemRentalId>[]>
var itemRentalResult = ItemRentalCollection
    .Select(x => x.Delete())
    .TakeAll()
    .Map(_ => Unit.Value);

// produces Result<Unit[], (DomainError<RetailSaleId, RetailSaleLineItemId>[], DomainError<RetailSaleId, RetailSaleItemRentalId>[])>
var intermediateResult = Result.ZipFailure(lineItemResult, itemRentalResult);

// produces Result<Unit, RetailSaleCannotBeDeletedDomainError>
var finalResult = intermediateResult
    .Map(_ => Unit.Value)
    .MapOnFailure(e => new RetailSaleCannotBeDeletedDomainError(e.Item1, e.Item2));
// some code omitted for brevity
public class RetailSaleCannotBeDeletedDomainError : DomainError<RetailSaleId>
{
    public IReadOnlyCollection<DomainError<RetailSaleId, RetailSaleLineItemId>> LineItemErrorCollection { get; }
    public IReadOnlyCollection<DomainError<RetailSaleId, RetailSaleItemRentalId>> ItemRentalErrorCollection { get; }
}
RyanMarcotte commented 1 week ago

To make things more generic, ZipFailure may need to produce Result<TSuccess[], (Option<TFailure1>, Option<TFailure2>)> instead.