Repository F# setup
open Reified
open Reified.Refinements
open Reified.Result
open Reified.Schema.JsonTransforming values
Two functions cover almost all of it. map changes the success value. bind runs a step that can itself fail.
open System
open Reified.Result
type SignupError =
| AgeMissing
| AgeNotANumber of string
| AgeOutOfRange of int
SystemReifiedResult03-result-handling_20-transforming.md_page.SignupErrorAgeMissingAgeNotANumberstringAn abbreviation for the CLI type . Basic Types
AgeOutOfRangeintAn abbreviation for the CLI type . Basic Types
map: the step cannot fail
map applies a plain function to the success value. An Error passes straight through untouched — the function never
runs.
Ok 36 |> Result.map (fun age -> age + 1)
// Ok 37
(Error AgeMissing: Result<int, SignupError>) |> Result.map (fun age -> age + 1)
// Error AgeMissing
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Reified.Result.ResultModuleFail-fast helpers over the standard F# Result type.
map: ('a -> 'b) -> Result<'a,'c> -> Result<'b,'c>Maps the success value of a result.
age: int(+): ^T1 -> ^T2 -> ^T3Overloaded addition operator The first parameter. The second parameter. The result of the operation. 2 + 2 // Evaluates to 4 "Hello " + "Word" // Evaluates to "Hello World"
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
AgeMissingMicrosoft.FSharp.Core.FSharpResult`2Helper type for error handling without exceptions. Choices and Results
intAn abbreviation for the CLI type . Basic Types
03-result-handling_20-transforming.md_page.SignupErrorbind: the step can fail
bind applies a function that returns a Result. Use it when the next step has its own way of failing.
let withinRange age =
if age < 130 then Ok age else Error (AgeOutOfRange age)
Ok 36 |> Result.bind withinRange // Ok 36
Ok 500 |> Result.bind withinRange // Error (AgeOutOfRange 500)
withinRange: int -> Result<int,SignupError>age: int(<): 'T -> 'T -> boolStructural less-than comparison The first parameter. The second parameter. The result of the comparison. 1 < 5 // Evaluates to true 5 < 5 // Evaluates to false (1, "a") < (1, "z") // Evaluates to true
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
AgeOutOfRange(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Reified.Result.ResultModuleFail-fast helpers over the standard F# Result type.
bind: ('a -> Result<'b,'c>) -> Result<'a,'c> -> Result<'b,'c>Binds a result to the next fail-fast operation.
The distinction is only the return type of the function you pass. If it returns 'b, use map. If it returns
Result<'b, _>, use bind. Using map with a Result-returning function gives you a nested
Result<Result<_,_>,_>, which is the usual sign you wanted bind.
Composing a pipeline
Because each helper takes the Result last, steps chain with |>:
let parseAge (raw: string) =
Int32.TryParse raw
|> Result.fromTry
|> Result.orError (AgeNotANumber raw)
|> Result.bind (fun age ->
if age >= 0 && age < 130 then Ok age else Error (AgeOutOfRange age))
parseAge: string -> Result<int,SignupError>raw: stringstringAn abbreviation for the CLI type . Basic Types
System.Int32Represents a 32-bit signed integer.
TryParse: string * byref<int> -> boolConverts the string representation of a number to its 32-bit signed integer equivalent. A return value indicates whether the conversion succeeded. A string containing a number to convert. When this method returns, contains the 32-bit signed integer value equivalent of the number contained in , if the conversion succeeded, or zero if the conversion failed. The conversion fails if the parameter is or , is not of the correct format, or represents a number less than Int32.MinValue or greater than Int32.MaxValue. This parameter is passed uninitialized; any value originally supplied in will be overwritten. if was converted successfully; otherwise, .
(|>): 'T1 -> ('T1 -> 'U) -> 'UApply a function to a value, the value being on the left, the function on the right The argument. The function. The function result. let doubleIt x = x * 2 3 |> doubleIt // Evaluates to 6
Reified.Result.ResultModuleFail-fast helpers over the standard F# Result type.
fromTry: bool * 'value -> Result<'value,unit>Converts a .NET Try* tuple into a unit-error result.
orError: 'error -> Result<'value,'discardedError> -> Result<'value,'error>Replaces whatever error a result carries with the supplied typed error. Ok passes through unchanged. The natural follow-up to okIf/failIf, which fail with unit precisely so the reason is chosen here: value |> Result.okIf isValid |> Result.orError MyError. Use Result.mapError instead when the existing error carries something worth keeping, as a Violation does.
AgeNotANumberbind: ('a -> Result<'b,'c>) -> Result<'a,'c> -> Result<'b,'c>Binds a result to the next fail-fast operation.
age: int(>=): 'T -> 'T -> boolStructural greater-than-or-equal The first parameter. The second parameter. The result of the comparison. 5 >= 1 // Evaluates to true 5 >= 5 // Evaluates to true [1; 5] >= [1; 6] // Evaluates to false
(&&): bool -> bool -> boolBinary 'and'. When used as a binary operator the right hand value is evaluated only on demand The first value. The second value. The result of the operation.
(<): 'T -> 'T -> boolStructural less-than comparison The first parameter. The second parameter. The result of the comparison. 1 < 5 // Evaluates to true 5 < 5 // Evaluates to false (1, "a") < (1, "z") // Evaluates to true
OkRepresents an OK or a Successful result. The code succeeded with a value of 'T.
ErrorRepresents an Error or a Failure. The code failed with a value of 'TError representing what went wrong.
AgeOutOfRangeThree outcomes, one for each way through:
parseAge "36" // Ok 36
parseAge "abc" // Error (AgeNotANumber "abc")
parseAge "500" // Error (AgeOutOfRange 500)
parseAge: string -> Result<int,SignupError>The chain short-circuits. Once a step produces Error, every later map and bind is skipped and that first error
is what comes out. Nothing downstream needs to test for it.
When a pipeline grows past two or three steps, or when a later step needs a value bound several steps earlier, the result computation expression says the same thing without the nesting.