Repository F# setup
open Reified
open Reified.Refinements
open Reified.Result

Extracting values

At the edge of a pipeline, extract a plain value when the caller does not handle Result.

Verified
open Reified.Result

type SignupError = AgeMissing

A default

defaultValue returns the success value, or the fallback you supply.

Verified
Ok 36 |> Result.defaultValue 0                                   // 36
(Error AgeMissing: Result<int, SignupError>) |> Result.defaultValue 0   // 0
Requires & types
Requires: open Reified.Result (packages: Reified.Result)

An option

toOption and toValueOption drop the error entirely.

Verified
Ok 36 |> Result.toOption                                          // Some 36
(Error AgeMissing: Result<int, SignupError>) |> Result.toOption    // None
(Error AgeMissing: Result<int, SignupError>) |> Result.toValueOption // ValueNone
Requires & types
Requires: open Reified.Result (packages: Reified.Result)

These discard why the operation failed. Use them when the caller does not need the reason: once the error is gone, it cannot be reported, logged, or returned to a user.

Pattern matching

Pattern match when the final branch handles both sides:

Verified
open System

let parseAge (raw: string) =
    Int32.TryParse raw
    |> Result.fromTry
    |> Result.orError AgeMissing

match parseAge "abc" with
| Ok age -> printfn "age is %d" age
| Error failure -> printfn "rejected: %A" failure
Requires & types
Requires: open Reified.Result; open System (packages: Reified.Result)
val parseAge: string -> Result<int,SignupError>

A Result from this package is the standard F# type, so every existing technique applies, matching, function shorthand, active patterns.

Keeping the error

If you need the value and the reason it might be missing, do not extract at all. Keep the Result until the last possible point, and let the caller decide. Extracting early is what forces the error back into a null, a sentinel, or an exception.