Errors as values, the Fusion mechanicsCombine, and when an exception is still right
No narration yet
Module 2 · Lesson 312 min

Combine, and when an exception is still right

When you build one thing out of several independently-validated pieces, you don't want a nest of if (a.IsFailure) checks. Result.Combine takes several Results and gives you one: if any failed, you get the union of all their errors; if all succeeded, you get a success carrying a tuple of the values. It's FailIf's cousin for whole Results instead of boolean conditions.

Combining validated pieces (FlightDisruptionAggregate.cs)
var combinedResult = Result.Combine(
    Result.Combine(delay3hrResult, delayEtdResult, cancellationResult, crewHotacResult, lostContributionResult),
    Result.Combine(delay3HrsWelfareResult, overnightWelfareResult, cancellationWelfareResult));

if (!combinedResult.IsSuccess)
{
    return Result<FlightDisruptionAggregate>.Failure(combinedResult.Errors.ToArray());
}

var ((delay3hr, delayEtd, cancellation, crewHotac, lostContribution),
     (delay3HrsWelfare, overnightWelfare, cancellationWelfare)) = combinedResult.Value;

So if validation never throws, when IS an exception right? When something genuinely unexpected happens: a bug, a broken invariant, a state the code should be unable to reach. Invalid user input is expected and routine; a Result handles it. A switch arm that should be unreachable is a bug; that throws.

You'll see this line in the real Entity base too: calling Equals on an entity throws NotImplementedException with the message "Don't use equality operators with entities." That's a deliberate, exceptional guard against a category of mistake, not validation. It's the right use of throw.

Practice

Try it yourself

Check

Predict Combine's output

Verify your mental model of the union behaviour.

You should see

Given Result.Combine(a, b, c) where a failed with error E1, b succeeded, and c failed with error E3, the combined Result is a failure whose Errors contains both E1 and E3 (the union), and its Value is inaccessible. If all three had succeeded, the Result would be a success carrying the tuple (a.Value, b.Value, c.Value).

Quiz

Result or throw

Which of these should be an exception, not a Result?