FSharpPlus


Computations Expressions

This library allows to use some common computation expressions without writing any boiler plate code.

There is a single computation expression: monad but it comes in 4 flavours:

  • Delayed or strict

    Delayed computations require that the type implements a Delay method. F# comes with async and seq computation expressions, both are delayed.

  • It can have embedded side-effects or act as a monadplus

    A monadplus can return (or yield) many times, so for example all expressions in a loop can be returned, whereas in the other model those expressions are of type unit, since a side effect is expected.

    Async workflows is an example of a side-effect computation expression and seq expressions are an example of monadplus.

    Side effect workflows don't have any additional requirement over the type (apart from the monad operations), but monadplus requires the additional get_Empty and (<|>) methods.

    The generic computation expression monad is a side-effect one, but it can be turned into a monadplus by accessing the .plus property.

    These computations are lazy by default, but they can be made strict by adding .strict or using a ', ie monad.plus'.

Examples

You may run this script step-by-step.

 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: 
52: 
53: 
54: 
55: 
56: 
57: 
58: 
59: 
60: 
61: 
#r @"../../src/FSharpPlus/bin/Release/net45/FSharpPlus.dll"
open FSharpPlus

let lazyValue = monad {
    let! a = lazy (printfn "I'm lazy"; 2)
    let! b = lazy (printfn "I'm lazy too"; 10)
    return a + b}

// val lazyValue : System.Lazy<int> = Value is not created.

let res12 = lazyValue.Value


let maybeWithSideFx = monad' { 
    let! a = Some 3
    let b = ref 0
    while !b < 10 do 
        let! n = Some ()
        incr b
    if a = 3 then printfn "got 3"
    else printfn "got something else (will never print this)"
    return a }

// val maybeWithSideFx : int option = Some 3



let lst = [None; None; Some 2; Some 4; Some 10; None]

let maybeManyTimes = monad.plus' {
    let defaultValue = 42
    let mutable i = 0
    return! None
    while i < 5 do
        printfn "looping %i" i
        i <- i + 1
        return! lst.[i]
    printfn "halfway"
    return! None
    printfn "near the end"
    return defaultValue }

// val maybeManyTimes : int option = Some 2


let (asnNumber: Async<_>) = monad.fx {
    let mutable m = ResizeArray ()
    try
        for i = 1 to 10 do
            m.Add i
        return m.[-1]
    with e ->
        return -3 }


let (lstNumber: list<_>) = monad.plus' {
    try
        for i = 1 to 10 do
            return i
    with e ->
        return -3 }
namespace FSharpPlus
val monad : MonadFxBuilder
val a : 'a (requires member ( + ))
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
val b : 'a (requires member ( + ))
val res12 : obj
val monad' : MonadFxStrictBuilder
val a : int
union case Option.Some: Value: 'T -> Option<'T>
val b : int ref
Multiple items
val ref : value:'T -> 'T ref

--------------------
type 'T ref = Ref<'T>
val n : 'a
val incr : cell:int ref -> unit
val lst : int option list
union case Option.None: Option<'T>
val maybeManyTimes : obj option
property MonadFxBuilder.plus': MonadPlusStrictBuilder
val defaultValue : int
val mutable i : int
Multiple items
module Async

from FSharpPlus

--------------------
type Async =
  static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
  static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
  static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
  static member AwaitTask : task:Task -> Async<unit>
  static member AwaitTask : task:Task<'T> -> Async<'T>
  static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
  static member CancelDefaultToken : unit -> unit
  static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
  static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  ...

--------------------
type Async<'T> =
property MonadFxBuilder.fx: MonadFxBuilder
val mutable m : System.Collections.Generic.List<int>
type ResizeArray<'T> = System.Collections.Generic.List<'T>
val i : int
System.Collections.Generic.List.Add(item: int) : unit
val e : exn
val lstNumber : obj list
type 'T list = List<'T>
Fork me on GitHub