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:

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 @"../../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 }


(*
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

--------------------
[<Struct>]
type OptionT<'monad<option<'t>>> =
  | OptionT of 'monad<option<'t>>
    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)
    static member Local : OptionT<'MonadReader<'R2,'T>> * f:('R1 -> 'R2) -> OptionT<'a4> (requires member Local)
    static member Pass : m:OptionT<'a1> -> OptionT<'MonadWriter<'Monoid, option<'T>>> (requires member ( >>= ) and member Map and member Return and member Pass and member Return)
    static member Put : x:'S -> OptionT<'MonadState<unit,'S>> (requires member Return and member ( >>= ) and member Map and member Put)
    static member Return : x:'T -> OptionT<'Monad<option<'T>> (requires member Return)
    static member Tell : w:'Monoid -> OptionT<'MonadWriter<'Monoid, unit>> (requires member Return and member ( >>= ) and member Map and member Tell)
    ...
type 'T list = List<'T>
type unit = Unit
Multiple items
val option : f:('g -> 'h) -> n:'h -> _arg1:'g option -> 'h

--------------------
type 'T option = Option<'T>
val monad<'monad<'t>> : MonadFxBuilder<'monad<'t>>
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
val monad'<'monad<'t>> : MonadFxStrictBuilder<'monad<'t>>
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

--------------------
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 Choice : computations:seq<Async<'T option>> -> Async<'T option>
  static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
  ...

--------------------
type Async<'T> =
val mutable m : ResizeArray<int>
Multiple items
module ResizeArray

from FSharpPlus

--------------------
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