TP

Stateful computations in F# with update monads

Most discussions about monads, even in F#, start by looking at the well-known standard monads for handling state from Haskell. The reader monad gives us a way to propagate some read-only state, the writer monad makes it possible to (imperatively) produce values such as logs and the state monad encapsulates state that can be read and changed.

These are no doubt useful in Haskell, but I never found them as important for F#. The first reason is that F# supports state and mutation and often it is just easier to use a mutable state. The second reason is that you have to implement three different computation builders for them. This does not fit very well with the F# style where you need to name the computation explicitly, e.g. by writing async { ... } (See also my recent blog about the F# Computation Zoo paper).

When visiting the Tallinn university in December (thanks to James Chapman, Juhan Ernits & Tarmo Uustalu for hosting me!), I discovered the work on update monads by Danel Ahman and Tarmo Uustalu, which elegantly unifies reader, writer and state monads using a single abstraction.

In this article, I implement the idea of update monads in F#. Update monads are parameterized by acts that capture the operations that can be done on the state. This means that we can define just a single computation expression update { ... } and use it for writing computations of all three aforementioned kinds.

Introducing update monads

Before looking at the definition of update monads, it is useful to review the three monads that we want to unify. The update monads paper has more details and you can also find other tutorials that implement these in F# - here, we'll only look at the type definitions:

1: 
2: 
3: 
4: 
5: 
6: 
/// Given a readonly state, produces a value
type Reader<'TState, 'T> = 'TState -> 'T
/// Produces a value together with additional state
type Writer<'TState, 'T> = 'TState * 'T
/// Given state, produces new state & a value
type State<'TState, 'T>  = 'TState -> 'TState * 'T

If you look at the definitions, it looks like reader and writer are both a versions of the state with some aspect missing - reader does not produce a new state and writer does not take previous state.

How can we define a parameterized computation type that allows leaving one or the other out? The idea of update monads is quite simple. The trick is that we'll take two different types - one representing the state we can read and another representing the updates that specify how to change the state:

1: 
2: 
3: 
4: 
/// Represents an update monad - given a state, produce 
/// value and an update that can be applied to the state
type UpdateMonad<'TState, 'TUpdate, 'T> = 
  UM of ('TState -> 'TUpdate * 'T)

To make the F# implementation a bit nicer, this is not defined as a type alias, but as a new type labeled with UM (this makes sure that the inferred types will always use the name UpdateMonad for the type, rather than its definition).

To make this work, we also need some operations on the types representing states and updates. In particular, we need:

In more formal terms, the type of updates needs to be a monoid (with unit and composition). In the paper, the two types (sets) together with the operations are called act and are defined as \((S, (P,\circ,\oplus), \downarrow)\) where \(\circ\) is the unit, \(\oplus\) is composition and \(\downarrow\) is the application.

Note on naming

In the last case, I'm using different naming than the original paper. In the paper, applying update \(u\) to a state \(s\) is written as \(s \downarrow u\). You can see the "\(\downarrow u\)" part as an action that transforms state and so the authors use the name action. I'm going to use apply instead because I refer more to the operation \(\downarrow\) than to the entire (partially-applied action).

Implementing update monads in F#

To implement this idea in F#, we can use static member constraints. If you do not know about static member constrains in F#, you can think of it as a form of duck typing (or lightweight type classes). If a type defines certain members, then the code will compile and run fine, otherwise you'll get a compile-time error. We will require the user to define two types representing State and Update, respectively. The Update type will need to define the three operations. An abstract definition (not valid F# code) would look like this:

type State
type Update =
  static Unit    : Update
  static Combine : Update * Update -> Update
  static Apply   : State * Update -> State

Invocation of members via static member constraints is not entirely easy (the feature is used mainly by library implementors for things like generic numerical code). But the idea is that we can define inline functions (unit, ++ and apply) that invoke the corresponding operation on the type (specified either explicitly or via type inference).

If you're not familiar with F#, you can freely skip over this definition, just remember that we now have functions unit and apply and an operator ++:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
/// Returns the value of 'Unit' property on the ^S type
let inline unit< ^S when ^S : 
    (static member Unit : ^S)> () : ^S =
  (^S : (static member Unit : ^S) ()) 

/// Invokes Combine operation on a pair of ^S values
let inline (++)< ^S when ^S : 
    (static member Combine : ^S * ^S -> ^S )> a b : ^S =
  (^S : (static member Combine : ^S * ^S -> ^S) (a, b)) 

/// Invokes Apply operation on state and update ^S * ^U
let inline apply< ^S, ^U when ^U : 
    (static member Apply : ^S * ^U -> ^S )> s a : ^S =
  (^U : (static member Apply : ^S * ^U -> ^S) (s, a)) 

The last thing that we need to do before we can start playing with some update monads is to implement the monadic operators. In F#, this is done by defining a computation builder - a type that has Bind and Return operations (as well as some others that we'll see later). The compiler then automatically translates a block update { .. } using operations update.Return and update.Bind.

The computation builder is a normal class with members. Because we are using static member constraints and inline functions, we need to mark the members as inline too:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
type UpdateBuilder() = 
  /// Returns the specified value, together
  /// with empty update obtained using 'unit'
  member inline x.Return(v) : UpdateMonad<'S, 'U, 'T> = 
    UM (fun s -> (unit(),v))

  /// Compose two update monad computations
  member inline x.Bind(UM u1, f:'T -> UpdateMonad<'S, 'U, 'R>) =  
    UM (fun s -> 
      // Run the first computation to get first update
      // 'u1', then run 'f' to get second computation
      let (u1, x) = u1 s
      let (UM u2) = f x
      // Apply 'u1' to original state & run second computation
      // then return result with combined state updates
      let (u2, y) = u2 (apply s u1)
      (u1 ++ u2, y))

/// Instance of the computation builder
/// that defines the update { .. } block
let update = UpdateBuilder()

The implementation of the Return operation is quite simple - we return the specified value and call unit() to get the unit of the monoid of updates - as a result, we get a computation that returns the value without performing any update on the state.

The Bind member is more interesting - it runs the first computation which returns a value x and an update u1. The second computation needs to be run in an updated state and so we use apply s u1 to calculate a new state that reflects the update. After running the second computation, we get the final resulting value y and a second update u2. The result of the computation combines the two updates using u1 ++ u2.

How does this actually work? Let's start by looking at reader and writer monads (which are special cases of the update monad.

Implementing the reader monad

The reader monad keeps some state, but it does not give us any way of modifying it. In terms of update monads, this means that there is some state, but the monoid of updates is trivial - in principle, we can just use unit as the type of updates. You can see that when looking at the type too - the type of reader monad is 'TState -> 'T. To get a type with a structure matching to update monads, we can use an equivalent type 'TState -> unit * 'T.

Reader state and update

In practice, we still need to define a type for updates, so that we can provide the required static members. We use a single-case discriminated union with just one value NoUpdate:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
/// The state of the reader is 'int'
type ReaderState = int
/// Trivial monoid of updates 
type ReaderUpdate = 
  | NoUpdate
  static member Unit = NoUpdate
  static member Combine(NoUpdate, NoUpdate) = NoUpdate
  static member Apply(s, NoUpdate) = s

None of the operations on the ReaderUpdate type does anything interesting. Both unit and combine simply returns the only possible value and the apply operation returns the state without a change.

Reader monad primitives

Next, we need a primitive that allows us to read the state and a run operation that executes a computation implemented using the reader monad (given the value of the read-only state). The operations look as follows:

1: 
2: 
3: 
4: 
5: 
/// Read the current state (int) and return it as 'int'
let read = UM (fun (s:ReaderState) -> 
  (NoUpdate, s))
/// Run computation and return the result 
let readRun (s:ReaderState) (UM f) = f s |> snd

When you look at the type of computations (hover the mouse pointer over the read identifier), you can see a parameterized update monad type. The read primitive has a type UpdateMonad<ReaderState, ReaderUpdate, ReaderState>. This means that we have an update monad that uses ReaderState and ReaderUpdate as the act (specifying the computation details) and, when executed, produces a value of ReaderState.

Sample reader computations

Now we can use the update { .. } block together with the read primitive to write computations that can read an immutable state. The following basic example reads the state and adds one (in demo1), and then adds 1 again in demo2:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
/// Returns state + 1
let demo1 = update { 
  let! v = read
  return v + 1 }
/// Returns the result of demo1 + 1
let demo2 = update { 
  let! v = demo1
  return v + 1 }

// Run it with state 40 
demo2 |> readRun 40

If you run the code, you'll see that the result is 42. The interesting thing about this approach is that we only had to define two types. The update { .. } computation works for all update monads and so we get the computation builder "for free". However, thanks to the parameterization, the computation really represents an immutable state - there is no way to mutate it.

Implementing the writer monad

Similarly to the reader monad, the writer monad is just a simple special case of the update monad. This time, the state is trivial and all the interesting things are happening in the updates. The type of the usual writer monad is 'TState * 'T and so if we want to make this a special case of update monad, we can define the type as unit -> 'TState * 'T.

Writer state and update

The state needs to be a monoid (with unit and composition) so that we can compose the states of multiple sub-computations. The following example uses a list as a concrete example. We define a (writer) monad that keeps a list of 'TLog values and returns that as the result (more generally, we could use an arbitrary monoid instead of a list):

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
/// Writer monad has no readable state
type WriterState = NoState

/// Updates of writer monad form a list
type WriterUpdate<'TLog> = 
  | Log of list<'TLog>
  /// Returns the empty log (monoid unit)
  static member Unit = Log []
  /// Combines two logs (operation of the monoid)
  static member Combine(Log a, Log b) = Log(List.append a b)
  /// Applying updates to state does not affect the state
  static member Apply(NoState, _) = NoState

The writer monad appears (in some informal sense) dual to the earlier reader monad. The state (that can be read) is always empty and is represented by the NoState value, while all the interesting aspects are captured by the WriterUpdate type - which is a list of values produced by the computation. The updates of a writer monad have to form a monoid - here, we use a list that concatenates all logged values. You could easily change the definition to implement other monoids (e.g. to keep the last produced value).

Writer monad primitives

Similarly to the previous example, we now need two primitives - one to add a new element to the log (write of the writer monad) and one to run a computation and extract the result and the log:

1: 
2: 
3: 
4: 
5: 
/// Writes the specified value to the log 
let write v = UM (fun s -> (Log [v], ()))
/// Runs a "writer monad computation" and returns 
/// the log, together with the final result
let writeRun (UM f) = let (Log l, v) = f NoState in l, v

The write function creates a singleton list containing the specified value Log [v] as the update and returns the unit value as the result of the computation. When combined with other computations, the updates are concatenated and so this will become a part of the list l in the result (Log l, v) that is made accessible in the writerRun function.

Sample writer computations

Let's have a look at a sample computation using the new definitions - the remarkable thing (from the practical F# programming perspective) is that we wrap the computation in the update { .. } block just like in the previous example. But this time, we'll use the write primitive to write 20 and then 10 to the log and the F# compiler correctly infers that we are using WriterState and WriterUpdate types:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
/// Writes '20' to the log and returns "world"
let demo3 = update {
  do! write 20
  return "world" }
/// Calls 'demo3' and then writes 10 to the log
let demo4 = update {
  let! w = demo3
  do! write 10
  return "Hello " + w }

/// Returns "Hello world" with 20 and 10 in the log
demo4 |> writeRun

If you run the code, the demo3 computation first writes 20 to the log, which is then combined (using the ++ operator that invokes WriterUpdate.Combine) with the value 10 written in demo4.

Building richer computations

One of the key things about F# computation expressions that I emphasized in my previous blog post and in the PADL 2014 paper is that computation expressions provide rich syntax that includes resource management (the use keyword), exception handling or loops (for and while) - in simple words, it mirrors the normal syntax of F#.

So far, we have not used any of these for update monads. All these additional constructs have to be provided in the computation builder (so that the author can define them in the most suitable way). The great thing about update monads (for F#) is that we have just a single computation builder and so we can define a number of additional operations to enable richer syntax.

The following snippet extends UpdateBuilder defined earlier with more operations. If you're not interested in the details, feel free to skip to the next section. The key idea is that this only has to be written once!

 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: 
/// Extends UpdateBuilder to support additional syntax
type UpdateBuilder with
  /// Represents monadic computation that returns unit
  /// (e.g. we can now omit 'else' branch in 'if' computation)
  member inline x.Zero() = x.Return(())

  /// Delays a computation with (uncontrolled) side effects
  member inline x.Delay(f) = x.Bind(x.Zero(), f)

  /// Sequential composition of two computations where the
  /// first one has no result (returns a unit value)
  member inline x.Combine(c1, c2) = x.Bind(c1, fun () -> c2)

  /// Enable the 'return!' keyword to return another computation
  member inline x.ReturnFrom(m : UpdateMonad<'S, 'P, 'T>) = m

  /// Ensure that resource 'r' is disposed of at the end of the
  /// computation specified by the function 'f'
  member inline x.Using(r,f) = UM(fun s -> 
    use rr = r in let (UM g) = f rr in g s)

  /// Support 'for' loop - runs body 'f' for each element in 'sq'
  member inline x.For(sq:seq<'V>, f:'V -> UpdateMonad<'S, 'P, unit>) = 
    let rec loop (en:System.Collections.Generic.IEnumerator<_>) = 
      if en.MoveNext() then x.Bind(f en.Current, fun _ -> loop en)
      else x.Zero()
    x.Using(sq.GetEnumerator(), loop)

  /// Supports 'while' loop - run body 'f' until condition 't' holds
  member inline x.While(t, f:unit -> UpdateMonad<'S, 'P, unit>) =
    let rec loop () = 
      if t() then x.Bind(f(), loop)
      else x.Zero()
    loop()

You can find more details about these operations in the F# Computation Zoo paper or in the F# language specification. In fact, the definitions mostly follow the samples from the F# specification. It is worth noting that all the members are marked as inline, which allows us to use static member constrains and to write code that will work for any update monad (as defined by a pair of update and state types).

Let's look at a trivial example using the writer computation:

1: 
2: 
3: 
4: 
/// Logs numbers from 1 to 10
let logNumbers = update {
  for i in 1 .. 10 do 
    do! write i }

As expected, when we run the computation using writeRun, the result is a tuple containing a list with numbers from 1 to 10 and a unit value. The computation does not explicitly return and so the Zero member is automatically used.

Implementing the state monad

Interestingly, the standard state monad is not a special case of update monads. However, we can define a computation that implements the same functionality - a computation with state that we can read and write.

States and updates

In this final example, both the type representing state and the type representing update will have a useful role. We make both of the types generic over the value they carry. State is simply a wrapper containing the value (current state). Update can be of two kinds - we have an empty update (do nothing) and an update to set the state:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
/// Wraps a state of type 'T
type StateState<'T> = State of 'T

/// Represents updates on state of type 'T
type StateUpdate<'T> = 
  | Set of 'T | SetNop
  /// Empty update - do not change the state
  static member Unit = SetNop
  /// Combine updates - return the latest (rightmost) 'Set' update
  static member Combine(a, b) = 
    match a, b with 
    | SetNop, v | v, SetNop -> v 
    | Set a, Set b -> Set b
  /// Apply update to a state - the 'Set' update changes the state
  static member Apply(s, p) = 
    match p with SetNop -> s | Set s -> State s

This definition is a bit more interesting than the previous two, because there is some interaction between the states and updates. In particular, when the update is Set v (we want to replace the current state with a new one), the Apply member returns a new state instead of the original. For the Unit member, we need an update SetNop which simply means that we want to keep the original state (and so Apply just returns the original value in this case).

Another notable thing is the Combine operation - it takes two updates (which may be either empty updates or set updates) and produces a single one. If you read a composition a1 ++ a2 ++ .. ++ an as a sequence of state updates (either Set or SetNop), then the Combine operation returns the last Set update in the sequence (or SetNop if there are no Set updates). In other words, it builds an update that sets the last state that was set during the whole sequence.

State monad primitives

Now that we have the type definitions, it is quite easy to add the usual primitives:

1: 
2: 
3: 
4: 
5: 
6: 
/// Set the state to the specified value
let set s = UM (fun _ -> (Set s,()))
/// Get the current state 
let get = UM (fun (State s) -> (SetNop, s))
/// Run a computation using a specified initial state
let setRun s (UM f) = f (State s) |> snd

The set operation is a bit different than the usual one for state monad. It ignores the state and it builds an update that tells the computation to set the new state. The get operation reads the state and returns it - but as it does not intend to change it, it returns SetNop as the update.

Sample stateful computation

If you made it this far in the article, you can already expect how the example will look! We'll again use the update { .. } computation. This time, we define a computation demo5 that increments the state and call it from a loop in demo6:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
/// Increments the state by one
let demo5 = update { 
  let! v = get
  do! set (v + 1) }
/// Call 'demo5' repeatedly in a loop
/// and then return the final state
let demo6 = update {
  for i in 1 .. 10 do 
    do! demo5
  return! get }
// Run the sample with initial state 0
demo6 |> setRun 0

Running the code yields 10 as expected - we start with zero and then increment the state ten times. Since we extended the definition of the UpdateBuilder (in the previous section), we now got a few nice things for free - we can use the for loop and write computations (like demo5) without explicit return if they just need to modify the state.

Conclusions

People coming to F# from the Haskell background often dislike the fact that F# does not let you write code polymorphic over monads and that computation expressions always explicitly state the type of computations such as async { .. }. I think there are good reasons for this and tried to explain some of them in a recent blog post and PADL paper.

As a result, using reader, writer and state monads in F# was always a bit cumbersome. In this blog post, I looked at an F# implementation of the recent idea called update monads (see the original paper (PDF)), which unifies the three state-related monads into a single type. This works very nicely with F# - we can define just a single computation builder for all state-related computations and then define a concrete state-related monad by defining two simple types. I used the approach to define a reader monad, writer monad useful for logging and a state monad (that keeps a state and allows changing it).

I guess that making update monads part of standard library and standard programming style in Haskell will be tricky because of historical reasons. However, for F# libraries that try to make purely functional programming easier, I think that update monads are the way to go.

type Reader<'TState,'T> = 'TState -> 'T

Full name: Update-monads.Reader<_,_>


 Given a readonly state, produces a value
type Writer<'TState,'T> = 'TState * 'T

Full name: Update-monads.Writer<_,_>


 Produces a value together with additional state
type State<'TState,'T> = 'TState -> 'TState * 'T

Full name: Update-monads.State<_,_>


 Given state, produces new state & a value
type UpdateMonad<'TState,'TUpdate,'T> = | UM of ('TState -> 'TUpdate * 'T)

Full name: Update-monads.UpdateMonad<_,_,_>


 Represents an update monad - given a state, produce
 value and an update that can be applied to the state
union case UpdateMonad.UM: ('TState -> 'TUpdate * 'T) -> UpdateMonad<'TState,'TUpdate,'T>
Multiple items
val unit : unit -> 'S (requires member get_Unit)

Full name: Update-monads.unit

--------------------
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val a : 'S (requires member Combine)
val b : 'S (requires member Combine)
val apply : s:'S -> a:'U -> 'S (requires member Apply)

Full name: Update-monads.apply
val s : 'S
val a : 'U (requires member Apply)
Multiple items
type UpdateBuilder =
  new : unit -> UpdateBuilder
  member Bind : UpdateMonad<'S,'U,'T> * f:('T -> UpdateMonad<'S,'U,'R>) -> UpdateMonad<'S,'U,'R> (requires member Combine and member Apply)
  member Combine : c1:UpdateMonad<'a,'b,unit> * c2:UpdateMonad<'a,'b,'c> -> UpdateMonad<'a,'b,'c> (requires member Combine and member Apply)
  member Delay : f:(unit -> UpdateMonad<'a,'b,'c>) -> UpdateMonad<'a,'b,'c> (requires member get_Unit and member Combine and member Apply)
  member For : sq:seq<'V> * f:('V -> UpdateMonad<'S,'P,unit>) -> UpdateMonad<'S,'P,unit> (requires member Combine and member Apply and member get_Unit)
  member Return : v:'T -> UpdateMonad<'S,'U,'T> (requires member get_Unit)
  member ReturnFrom : m:UpdateMonad<'S,'P,'T> -> UpdateMonad<'S,'P,'T>
  member Using : r:'a * f:('a -> UpdateMonad<'b,'c,'d>) -> UpdateMonad<'b,'c,'d> (requires 'a :> IDisposable)
  member While : t:(unit -> bool) * f:(unit -> UpdateMonad<'S,'P,unit>) -> UpdateMonad<'S,'P,unit> (requires member Combine and member Apply and member get_Unit)
  member Zero : unit -> UpdateMonad<'a,'b,unit> (requires member get_Unit)

Full name: Update-monads.UpdateBuilder

--------------------
new : unit -> UpdateBuilder
val x : UpdateBuilder
member UpdateBuilder.Return : v:'T -> UpdateMonad<'S,'U,'T> (requires member get_Unit)

Full name: Update-monads.UpdateBuilder.Return


 Returns the specified value, together
 with empty update obtained using 'unit'
val v : 'T
member UpdateBuilder.Bind : UpdateMonad<'S,'U,'T> * f:('T -> UpdateMonad<'S,'U,'R>) -> UpdateMonad<'S,'U,'R> (requires member Combine and member Apply)

Full name: Update-monads.UpdateBuilder.Bind


 Compose two update monad computations
val u1 : ('S -> 'U * 'T) (requires member Combine and member Apply)
val f : ('T -> UpdateMonad<'S,'U,'R>) (requires member Combine and member Apply)
val u1 : 'U (requires member Combine and member Apply)
val x : 'T
val u2 : ('S -> 'U * 'R) (requires member Combine and member Apply)
val u2 : 'U (requires member Combine and member Apply)
val y : 'R
val update : UpdateBuilder

Full name: Update-monads.update


 Instance of the computation builder
 that defines the update { .. } block
type ReaderState = int

Full name: Update-monads.ReaderState


 The state of the reader is 'int'
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<_>
type ReaderUpdate =
  | NoUpdate
  static member Apply : s:'a * NoUpdate:ReaderUpdate -> 'a
  static member Combine : NoUpdate:ReaderUpdate * NoUpdate:ReaderUpdate -> ReaderUpdate
  static member Unit : ReaderUpdate

Full name: Update-monads.ReaderUpdate


 Trivial monoid of updates
union case ReaderUpdate.NoUpdate: ReaderUpdate
static member ReaderUpdate.Unit : ReaderUpdate

Full name: Update-monads.ReaderUpdate.Unit
static member ReaderUpdate.Combine : NoUpdate:ReaderUpdate * NoUpdate:ReaderUpdate -> ReaderUpdate

Full name: Update-monads.ReaderUpdate.Combine
static member ReaderUpdate.Apply : s:'a * NoUpdate:ReaderUpdate -> 'a

Full name: Update-monads.ReaderUpdate.Apply
val s : 'a
val read : UpdateMonad<ReaderState,ReaderUpdate,ReaderState>

Full name: Update-monads.read


 Read the current state (int) and return it as 'int'
val s : ReaderState
val readRun : s:ReaderState -> UpdateMonad<ReaderState,'a,'b> -> 'b

Full name: Update-monads.readRun


 Run computation and return the result
val f : (ReaderState -> 'a * 'b)
val snd : tuple:('T1 * 'T2) -> 'T2

Full name: Microsoft.FSharp.Core.Operators.snd
val demo1 : UpdateMonad<ReaderState,ReaderUpdate,ReaderState>

Full name: Update-monads.demo1


 Returns state + 1
val v : ReaderState
val demo2 : UpdateMonad<ReaderState,ReaderUpdate,ReaderState>

Full name: Update-monads.demo2


 Returns the result of demo1 + 1
type WriterState = | NoState

Full name: Update-monads.WriterState


 Writer monad has no readable state
union case WriterState.NoState: WriterState
type WriterUpdate<'TLog> =
  | Log of 'TLog list
  static member Apply : NoState:WriterState * 'a -> WriterState
  static member Combine : WriterUpdate<'a> * WriterUpdate<'a> -> WriterUpdate<'a>
  static member Unit : WriterUpdate<int>

Full name: Update-monads.WriterUpdate<_>


 Updates of writer monad form a list
union case WriterUpdate.Log: 'TLog list -> WriterUpdate<'TLog>
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
static member WriterUpdate.Unit : WriterUpdate<int>

Full name: Update-monads.WriterUpdate`1.Unit


 Returns the empty log (monoid unit)
static member WriterUpdate.Combine : WriterUpdate<'a> * WriterUpdate<'a> -> WriterUpdate<'a>

Full name: Update-monads.WriterUpdate`1.Combine


 Combines two logs (operation of the monoid)
val a : 'a list
val b : 'a list
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val append : list1:'T list -> list2:'T list -> 'T list

Full name: Microsoft.FSharp.Collections.List.append
static member WriterUpdate.Apply : NoState:WriterState * 'a -> WriterState

Full name: Update-monads.WriterUpdate`1.Apply


 Applying updates to state does not affect the state
val write : v:'a -> UpdateMonad<'b,WriterUpdate<'a>,unit>

Full name: Update-monads.write


 Writes the specified value to the log
val v : 'a
val s : 'b
val writeRun : UpdateMonad<WriterState,WriterUpdate<'a>,'b> -> 'a list * 'b

Full name: Update-monads.writeRun


 Runs a "writer monad computation" and returns
 the log, together with the final result
val f : (WriterState -> WriterUpdate<'a> * 'b)
val l : 'a list
val v : 'b
val demo3 : UpdateMonad<WriterState,WriterUpdate<int>,string>

Full name: Update-monads.demo3


 Writes '20' to the log and returns "world"
val demo4 : UpdateMonad<WriterState,WriterUpdate<int>,string>

Full name: Update-monads.demo4


 Calls 'demo3' and then writes 10 to the log
val w : string
member UpdateBuilder.Zero : unit -> UpdateMonad<'a,'b,unit> (requires member get_Unit)

Full name: Update-monads.UpdateBuilder.Zero


 Represents monadic computation that returns unit
 (e.g. we can now omit 'else' branch in 'if' computation)
member UpdateBuilder.Return : v:'T -> UpdateMonad<'S,'U,'T> (requires member get_Unit)


 Returns the specified value, together
 with empty update obtained using 'unit'
member UpdateBuilder.Delay : f:(unit -> UpdateMonad<'a,'b,'c>) -> UpdateMonad<'a,'b,'c> (requires member get_Unit and member Combine and member Apply)

Full name: Update-monads.UpdateBuilder.Delay


 Delays a computation with (uncontrolled) side effects
val f : (unit -> UpdateMonad<'a,'b,'c>) (requires member get_Unit and member Combine and member Apply)
member UpdateBuilder.Bind : UpdateMonad<'S,'U,'T> * f:('T -> UpdateMonad<'S,'U,'R>) -> UpdateMonad<'S,'U,'R> (requires member Combine and member Apply)


 Compose two update monad computations
member UpdateBuilder.Zero : unit -> UpdateMonad<'a,'b,unit> (requires member get_Unit)


 Represents monadic computation that returns unit
 (e.g. we can now omit 'else' branch in 'if' computation)
member UpdateBuilder.Combine : c1:UpdateMonad<'a,'b,unit> * c2:UpdateMonad<'a,'b,'c> -> UpdateMonad<'a,'b,'c> (requires member Combine and member Apply)

Full name: Update-monads.UpdateBuilder.Combine


 Sequential composition of two computations where the
 first one has no result (returns a unit value)
val c1 : UpdateMonad<'a,'b,unit> (requires member Combine and member Apply)
val c2 : UpdateMonad<'a,'b,'c> (requires member Combine and member Apply)
member UpdateBuilder.ReturnFrom : m:UpdateMonad<'S,'P,'T> -> UpdateMonad<'S,'P,'T>

Full name: Update-monads.UpdateBuilder.ReturnFrom


 Enable the 'return!' keyword to return another computation
val m : UpdateMonad<'S,'P,'T>
member UpdateBuilder.Using : r:'a * f:('a -> UpdateMonad<'b,'c,'d>) -> UpdateMonad<'b,'c,'d> (requires 'a :> System.IDisposable)

Full name: Update-monads.UpdateBuilder.Using


 Ensure that resource 'r' is disposed of at the end of the
 computation specified by the function 'f'
val r : #System.IDisposable
val f : (#System.IDisposable -> UpdateMonad<'b,'c,'d>)
val rr : #System.IDisposable
val g : ('b -> 'c * 'd)
member UpdateBuilder.For : sq:seq<'V> * f:('V -> UpdateMonad<'S,'P,unit>) -> UpdateMonad<'S,'P,unit> (requires member Combine and member Apply and member get_Unit)

Full name: Update-monads.UpdateBuilder.For


 Support 'for' loop - runs body 'f' for each element in 'sq'
val sq : seq<'V>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Core.Operators.seq

--------------------
type seq<'T> = System.Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
val f : ('V -> UpdateMonad<'S,'P,unit>) (requires member Combine and member Apply and member get_Unit)
val loop : (System.Collections.Generic.IEnumerator<'V> -> UpdateMonad<'S,'P,unit>) (requires member Combine and member Apply and member get_Unit)
val en : System.Collections.Generic.IEnumerator<'V>
namespace System
namespace System.Collections
namespace System.Collections.Generic
type IEnumerator<'T> =
  member Current : 'T

Full name: System.Collections.Generic.IEnumerator<_>
System.Collections.IEnumerator.MoveNext() : bool
property System.Collections.Generic.IEnumerator.Current: 'V
member UpdateBuilder.Using : r:'a * f:('a -> UpdateMonad<'b,'c,'d>) -> UpdateMonad<'b,'c,'d> (requires 'a :> System.IDisposable)


 Ensure that resource 'r' is disposed of at the end of the
 computation specified by the function 'f'
System.Collections.Generic.IEnumerable.GetEnumerator() : System.Collections.Generic.IEnumerator<'V>
member UpdateBuilder.While : t:(unit -> bool) * f:(unit -> UpdateMonad<'S,'P,unit>) -> UpdateMonad<'S,'P,unit> (requires member Combine and member Apply and member get_Unit)

Full name: Update-monads.UpdateBuilder.While


 Supports 'while' loop - run body 'f' until condition 't' holds
val t : (unit -> bool)
val f : (unit -> UpdateMonad<'S,'P,unit>) (requires member Combine and member Apply and member get_Unit)
val loop : (unit -> UpdateMonad<'S,'P,unit>) (requires member Combine and member Apply and member get_Unit)
val logNumbers : UpdateMonad<WriterState,WriterUpdate<int>,unit>

Full name: Update-monads.logNumbers


 Logs numbers from 1 to 10
val i : int
type StateState<'T> = | State of 'T

Full name: Update-monads.StateState<_>


 Wraps a state of type 'T
Multiple items
union case StateState.State: 'T -> StateState<'T>

--------------------
type State<'TState,'T> = 'TState -> 'TState * 'T

Full name: Update-monads.State<_,_>


 Given state, produces new state & a value
type StateUpdate<'T> =
  | Set of 'T
  | SetNop
  static member Apply : s:StateState<'a> * p:StateUpdate<'a> -> StateState<'a>
  static member Combine : a:StateUpdate<'a> * b:StateUpdate<'a> -> StateUpdate<'a>
  static member Unit : StateUpdate<int>

Full name: Update-monads.StateUpdate<_>


 Represents updates on state of type 'T
Multiple items
union case StateUpdate.Set: 'T -> StateUpdate<'T>

--------------------
module Set

from Microsoft.FSharp.Collections

--------------------
type Set<'T (requires comparison)> =
  interface IComparable
  interface IEnumerable
  interface IEnumerable<'T>
  interface ICollection<'T>
  new : elements:seq<'T> -> Set<'T>
  member Add : value:'T -> Set<'T>
  member Contains : value:'T -> bool
  override Equals : obj -> bool
  member IsProperSubsetOf : otherSet:Set<'T> -> bool
  member IsProperSupersetOf : otherSet:Set<'T> -> bool
  ...

Full name: Microsoft.FSharp.Collections.Set<_>

--------------------
new : elements:seq<'T> -> Set<'T>
union case StateUpdate.SetNop: StateUpdate<'T>
static member StateUpdate.Unit : StateUpdate<int>

Full name: Update-monads.StateUpdate`1.Unit


 Empty update - do not change the state
static member StateUpdate.Combine : a:StateUpdate<'a> * b:StateUpdate<'a> -> StateUpdate<'a>

Full name: Update-monads.StateUpdate`1.Combine


 Combine updates - return the latest (rightmost) 'Set' update
val a : StateUpdate<'a>
val b : StateUpdate<'a>
val v : StateUpdate<'a>
val a : 'a
val b : 'a
static member StateUpdate.Apply : s:StateState<'a> * p:StateUpdate<'a> -> StateState<'a>

Full name: Update-monads.StateUpdate`1.Apply


 Apply update to a state - the 'Set' update changes the state
val s : StateState<'a>
val p : StateUpdate<'a>
val set : s:'a -> UpdateMonad<'b,StateUpdate<'a>,unit>

Full name: Update-monads.set


 Set the state to the specified value
val get : UpdateMonad<StateState<'a>,StateUpdate<'b>,'a>

Full name: Update-monads.get


 Get the current state
val setRun : s:'a -> UpdateMonad<StateState<'a>,'b,'c> -> 'c

Full name: Update-monads.setRun


 Run a computation using a specified initial state
val f : (StateState<'a> -> 'b * 'c)
val demo5 : UpdateMonad<StateState<int>,StateUpdate<int>,unit>

Full name: Update-monads.demo5


 Increments the state by one
val v : int
val demo6 : UpdateMonad<StateState<int>,StateUpdate<int>,int>

Full name: Update-monads.demo6


 Call 'demo5' repeatedly in a loop
 and then return the final state

Published: Tuesday, 13 May 2014, 4:41 PM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: f#, research, functional programming, monads