2 people like it.
Like the snippet!
"Guard" helper function for railway error handling
Defines a "guard" function for railway-style error handing which allows you to concisely verify a condition when handling errors using Choice<'T, 'Error>. It checks the condition and if it is false, returns Choice2Of2 with the specified error value. If the condition is true then it returns Choice1Of2 (). Loosely inspired by Swift 2.0's guard keyword.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
|
open ExtCore.Control
let succeed = Choice1Of2
let fail = Choice2Of2
let guard cond err = if cond then succeed () else fail err
[<Measure>] type GBP
type VendingMachineState =
{ Items : Map<string, int * float<GBP>>
Funds : float<GBP> }
type VendingMachineError =
| InvalidCashAmount of float<GBP>
| UnknownItem of string
| OutOfStock of string
| InsufficientFunds of float<GBP>
let findItem item vendingMachine =
match vendingMachine.Items |> Map.tryFind item with
| Some result -> succeed result
| None -> fail (UnknownItem item)
let payIn cash vendingMachine = choice {
do! guard (cash > 0.0<GBP>) (InvalidCashAmount cash)
return { vendingMachine with Funds = vendingMachine.Funds + cash } }
let vend item vendingMachine = choice {
let! (quantity, price) = findItem item vendingMachine
do! guard (quantity > 0) (OutOfStock item)
do! guard (vendingMachine.Funds >= price) (InsufficientFunds <| price - vendingMachine.Funds)
return (item,
{ vendingMachine with
Funds = vendingMachine.Funds - price
Items = vendingMachine.Items |> Map.add item (quantity - 1, price) }) }
let vendingMachine =
{ Items =
[("Candy bar", (4, 1.20<GBP>))
("Coke" , (0, 0.80<GBP>))
("Crisps" , (2, 0.55<GBP>))]
|> Map.ofList
Funds = 0.60<GBP> }
// note : vending machine is immutable: each of the transactions starts with the same initial state
// defined above and returns a new vending machine state (or error)
vendingMachine |> payIn 2.0<GBP> |> printfn "%A"
vendingMachine |> vend "Crisps" |> printfn "%A"
vendingMachine |> vend "Coke" |> printfn "%A"
vendingMachine |> vend "Diet coke" |> printfn "%A"
vendingMachine |> vend "Candy bar" |> printfn "%A"
|
namespace ExtCore
namespace ExtCore.Control
val succeed : arg0:'a -> Choice<'a,'b>
Full name: Script.succeed
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
val fail : arg0:'a -> Choice<'b,'a>
Full name: Script.fail
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
val guard : cond:bool -> err:'a -> Choice<unit,'a>
Full name: Script.guard
val cond : bool
val err : 'a
Multiple items
type MeasureAttribute =
inherit Attribute
new : unit -> MeasureAttribute
Full name: Microsoft.FSharp.Core.MeasureAttribute
--------------------
new : unit -> MeasureAttribute
[<Measure>]
type GBP
Full name: Script.GBP
type VendingMachineState =
{Items: Map<string,(int * float<GBP>)>;
Funds: float<GBP>;}
Full name: Script.VendingMachineState
VendingMachineState.Items: Map<string,(int * float<GBP>)>
Multiple items
module Map
from ExtCore.Collections
--------------------
module Map
from Microsoft.FSharp.Collections
--------------------
type Map<'Key,'Value (requires comparison)> =
interface IEnumerable
interface IComparable
interface IEnumerable<KeyValuePair<'Key,'Value>>
interface ICollection<KeyValuePair<'Key,'Value>>
interface IDictionary<'Key,'Value>
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
member Add : key:'Key * value:'Value -> Map<'Key,'Value>
member ContainsKey : key:'Key -> bool
override Equals : obj -> bool
member Remove : key:'Key -> Map<'Key,'Value>
...
Full name: Microsoft.FSharp.Collections.Map<_,_>
--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
--------------------
type string<'Measure> = string
Full name: ExtCore.string<_>
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
Multiple items
val float : value:'T -> float (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.float
--------------------
type float = System.Double
Full name: Microsoft.FSharp.Core.float
--------------------
type float<'Measure> = float
Full name: Microsoft.FSharp.Core.float<_>
VendingMachineState.Funds: float<GBP>
type VendingMachineError =
| InvalidCashAmount of float<GBP>
| UnknownItem of string
| OutOfStock of string
| InsufficientFunds of float<GBP>
Full name: Script.VendingMachineError
union case VendingMachineError.InvalidCashAmount: float<GBP> -> VendingMachineError
union case VendingMachineError.UnknownItem: string -> VendingMachineError
union case VendingMachineError.OutOfStock: string -> VendingMachineError
union case VendingMachineError.InsufficientFunds: float<GBP> -> VendingMachineError
val findItem : item:string -> vendingMachine:VendingMachineState -> Choice<(int * float<GBP>),VendingMachineError>
Full name: Script.findItem
val item : string
val vendingMachine : VendingMachineState
val tryFind : key:'Key -> table:Map<'Key,'T> -> 'T option (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.tryFind
union case Option.Some: Value: 'T -> Option<'T>
val result : int * float<GBP>
union case Option.None: Option<'T>
val payIn : cash:float<GBP> -> vendingMachine:VendingMachineState -> Choice<VendingMachineState,VendingMachineError>
Full name: Script.payIn
val cash : float<GBP>
val choice : ChoiceBuilder
Full name: ExtCore.Control.WorkflowBuilders.choice
val vend : item:string -> vendingMachine:VendingMachineState -> Choice<(string * VendingMachineState),VendingMachineError>
Full name: Script.vend
val quantity : int
val price : float<GBP>
val add : key:'Key -> value:'T -> table:Map<'Key,'T> -> Map<'Key,'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.add
val vendingMachine : VendingMachineState
Full name: Script.vendingMachine
val ofList : elements:('Key * 'T) list -> Map<'Key,'T> (requires comparison)
Full name: Microsoft.FSharp.Collections.Map.ofList
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
More information