Fail-fast composition with the result { } builder.
Result CE
Use the result {} computation expression when you have a sequence of steps where each step depends on the previous one, and you want to stop at the first failure.
This is “fail-fast” semantics.
Basic Usage
The result {} builder binds standard F# Result<'value, 'error> types.
type UserError = | MissingName | MissingEmail
let validateUser name email =
result {
// If name is blank, it returns Error MissingName and stops.
let! validName = name |> Check.notBlank |> Check.orError MissingName
// This line only runs if the name was valid.
let! validEmail = email |> Check.notBlank |> Check.orError MissingEmail
return { Name = validName; Email = validEmail }
}
Guarded Bindings
Guard is a powerful companion to result {}. It allows you to bind boolean or check-shaped sources directly while attaching an error value at the binding site.
let login username password =
result {
// If password is blank, fails with MissingPassword
do! Check.notBlank password |> Guard.Of MissingPassword
// If user not found, maps the underlying error to Unauthorized
let! user = tryGetUser username |> Guard.MapError (fun _ -> Unauthorized)
return user
}
When to use result {}
- Sequential Dependencies: When Step B requires the output of Step A.
- Fail-Fast: When continuing after an error makes no sense (e.g., you can’t save a user if the email is invalid).
- Simple Logic: When you only need to return a single error value to the caller.
If you need to collect multiple independent errors at once, use validate {} instead.