Result and Error Handling

Errors as values make failures explicit in signatures. Reserve exceptions (Module 24) for truly exceptional situations and .NET interop.

// type Result<'T, 'TError> = Ok of 'T | Error of 'TError
type ValidationError =
    | EmptyName
    | InvalidAge of int
    | InvalidEmail of string

let validateName (name: string) =
    if System.String.IsNullOrWhiteSpace name then Error EmptyName else Ok name

let validateAge age =
    if age < 0 || age > 150 then Error (InvalidAge age) else Ok age

let validateEmail (email: string) =
    if email.Contains "@" then Ok email else Error (InvalidEmail email)

type User = { Name: string; Age: int; Email: string }

let createUser name age email =
    validateName name
    |> Result.bind (fun n ->
        validateAge age
        |> Result.bind (fun a ->
            validateEmail email
            |> Result.map (fun e -> { Name = n; Age = a; Email = e })))

createUser "Ana" 30 "ana@x.com"     // Ok { Name = "Ana"; Age = 30; Email = "ana@x.com" }
createUser "" 30 "ana@x.com"        // Error EmptyName

// Result module
Ok 5 |> Result.map ((+) 1)                         // Ok 6
Error "bad" |> Result.mapError String.length       // Error 3
Ok 5 |> Result.defaultValue 0                      // 5 (F# 7+)
Result.isOk (Ok 1)                                 // true (F# 7+)
Ok 1 |> Result.toOpt
…