FSharpPlus


Computations Expressions

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

For applicatives there is single computation expression: applicative { .. }. Additionally applicative2 { .. } and applicative3 { .. } exists for composed (aka layered) applicatives.

For monadic code there is a single computation expression: monad { .. } but it comes in 4 flavours:

In other words:

Note that a type is either lazy or strict, but it could act as fx or plus at the same time (see below some examples). This means that we need to pay attention when using a CE over a type, if the type is lazy but with use a strict monad, we'll get strict semantics which probably would make no sense, but if we do the opposite we might run into runtime errors, fortunately a compile-time warning (or error) will prevent us.

A simple way to find out if a type is strict or lazy is to execute this in fsi: let _ : MyType<'t> = monad { printfn "I'm strict" }

For layered monads (monad transformers) the general rule is: the monad is strict unless at least one of its constituent types is lazy, in that case the whole monad becomes lazy.

let _ : OptionT<list<unit option>> = monad { printfn "I'm strict" }
// will print I'm strict, because OptionT and list are strict

let _ : OptionT<seq<unit option>> = monad { printfn "I'm strict" }
// won't print anything, because seq is lazy

Examples

You may run this script step-by-step.

#r @"nuget: FSharpPlus"
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 }


(*
For more information about computation expressions you can read the paper : The F# Computation Expression Zoo
http://tomasp.net/academic/papers/computation-zoo/computation-zoo.pdf
*)
namespace FSharpPlus
namespace FSharpPlus.Data
Multiple items
union case OptionT.OptionT: 'monad<option<'t>> -> OptionT<'monad<option<'t>>>

--------------------
module OptionT from FSharpPlus.Data
<summary> Basic operations on OptionT </summary>

--------------------
[<Struct>] type OptionT<'monad<option<'t>>> = | OptionT of 'monad<option<'t>> static member (+) : OptionT<'a1> * OptionT<'MonadPlus<option<'T>> -> OptionT<'MonadPlus<option<'T>> (requires member (>>=) and member (>>=) and member Return and member ``+``) static member (<*>) : f: OptionT<'Monad<option<('T -> 'U)>> * x: OptionT<'Monad<option<'T>> -> OptionT<'Monad<option<'U>> (requires member Map and member ``<*>``) static member (<|>) : OptionT<'a1> * OptionT<'MonadPlus<option<'T>> -> OptionT<'MonadPlus<option<'T>> (requires member (>>=) and member Return) static member (>=>) : f: ('T -> OptionT<'Monad<option<'U>>) * g: ('U -> OptionT<'Monad<option<'V>>) -> ('T -> OptionT<'Monad<option<'V>>) (requires member (>>=) and member Return) static member (>>=) : x: OptionT<'Monad<option<'T>> * f: ('T -> OptionT<'Monad<option<'U>>) -> OptionT<'Monad<option<'U>> (requires member (>>=) and member Return) static member CallCC: f: (('T -> OptionT<'MonadCont<'R,option<'U>>>) -> OptionT<'MonadCont<'R,option<'T>>>) -> OptionT<'MonadCont<'R,option<'T>>> (requires member CallCC) static member Catch: m: OptionT<'MonadError<'E1,'T>> * h: ('E1 -> OptionT<'MonadError<'E2,'T>>) -> OptionT<'MonadError<'E2,'T>> (requires member Catch) static member Delay: body: (unit -> OptionT<'Monad<option<'T>>>) -> OptionT<'Monad<option<'T>>> (requires member Delay) static member LiftAsync: x: Async<'T> -> OptionT<'MonadAsync<'T>> (requires member Return and member (>>=) and member Map and member LiftAsync) static member Listen: m: OptionT<'a1> -> OptionT<''MonadWriter<'Monoid, option<'T>>> (requires member (>>=) and member Return and member Listen) ...
<summary> Monad Transformer for Option&lt;'T&gt; </summary>
type 'T list = List<'T>
type unit = Unit
Multiple items
val option: f: ('g -> 'h) -> n: 'h -> _arg1: 'g option -> 'h
<summary> Takes a function, a default value and a option value. If the option value is None, the function returns the default value. Otherwise, it applies the function to the value inside Some and returns the result. </summary>
<category index="0">Common Combinators</category>


--------------------
type 'T option = Option<'T>
val monad<'monad<'t>> : MonadFxBuilder<'monad<'t>>
<summary> Creates a (lazy) monadic computation expression with side-effects (see http://fsprojects.github.io/FSharpPlus/computation-expressions.html for more information) </summary>
val printfn: format: Printf.TextWriterFormat<'T> -> 'T
Multiple items
val seq: sequence: seq<'T> -> seq<'T>

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
val lazyValue: System.Lazy<int>
val a: int
val b: int
val res12: int
property System.Lazy.Value: int with get
val maybeWithSideFx: int option
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: unit
val incr: cell: int ref -> unit
val lst: int option list
union case Option.None: Option<'T>
val maybeManyTimes: int option
val defaultValue: int
val mutable i: int
val asnNumber: Async<int>
Multiple items
module Async from FSharpPlus
<summary> Additional operations on Async </summary>

--------------------
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<'T> -> Async<'T> + 1 overload 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 Choice: computations: seq<Async<'T option>> -> Async<'T option> static member FromBeginEnd: beginAction: (AsyncCallback * obj -> IAsyncResult) * endAction: (IAsyncResult -> 'T) * ?cancelAction: (unit -> unit) -> Async<'T> + 3 overloads static member FromContinuations: callback: (('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T> ...

--------------------
type Async<'T>
val mutable m: ResizeArray<int>
Multiple items
module ResizeArray from FSharpPlus
<summary> Additional operations on ResizeArray </summary>

--------------------
type ResizeArray<'T> = System.Collections.Generic.List<'T>
val i: int
System.Collections.Generic.List.Add(item: int) : unit
val e: exn
val lstNumber: int list