The Writer monad is good way to introduce a log of a computation. It gives you a different way of logging that can be useful when you want to be able to inspect the logged results.


#r @"nuget: FSharpPlus"
open FSharpPlus
open FSharpPlus.Data
type LogEntry={msg:string}
    static member create x = {msg = x}

let output x =  Writer.tell [LogEntry.create x]

let calc = monad {
  do! output "I'm going to start a heavy computation" // start logging
  let y = sum [1..100_000]
  do! output (string y)
  do! output "The computation finished"
  return y // return the result of the computation

let logs = Writer.exec calc
let (y,logs') = Writer.run calc

There are some performance implications around using a regular list, that's why you should use DList in these scenarios

let output' x =  Writer.tell <| DList.ofSeq [LogEntry.create x]

let calc' = monad {
  do! output' "I'm going to start a heavy computation" // start logging
  let y = sum [1..100_000]
  do! output' (string y)
  do! output' "The computation finished"
  return y // return the result of the computation

let logs2 = Writer.exec calc'
let (y',logs2') = Writer.run calc'
namespace FSharpPlus
namespace FSharpPlus.Data
LogEntry.msg: string
Multiple items
val string: value: 'T -> string

type string = System.String
val x: string
val output: x: string -> Writer<LogEntry list,unit>
Multiple items
union case Writer.Writer: ('t * 'monoid) -> Writer<'monoid,'t>

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

[<Struct>] type Writer<'monoid,'t> = | Writer of ('t * 'monoid) static member ( *> ) : x: Writer<'Monoid,'T> * y: Writer<'Monoid,'U> -> Writer<'Monoid,'U> (requires member ``+``) static member (<!>) : f: ('T -> 'U) * x: Writer<'Monoid,'T> -> Writer<'Monoid,'U> static member ( <* ) : x: Writer<'Monoid,'U> * y: Writer<'Monoid,'T> -> Writer<'Monoid,'U> (requires member ``+``) static member (<*>) : f: Writer<'Monoid,('T -> 'U)> * x: Writer<'Monoid,'T> -> Writer<'Monoid,'U> (requires member ``+``) static member (=>>) : g: Writer<'T,'W> * f: (Writer<'T,'W> -> 'U) -> Writer<'U,'W> static member (>=>) : f: ('T -> Writer<'Monoid,'U>) * g: ('U -> Writer<'Monoid,'V>) -> ('T -> Writer<'Monoid,'V>) (requires member ``+``) static member (>>=) : x: Writer<'Monoid,'T> * f: ('T -> Writer<'Monoid,'U>) -> Writer<'Monoid,'U> (requires member ``+``) static member Extract: Writer<'T,'W> -> 'T static member Listen: m: Writer<'Monoid,'T> -> Writer<'Monoid,('T * 'Monoid)> static member Pass: m: Writer<'Monoid,('T * ('Monoid -> 'Monoid))> -> Writer<'Monoid,'T> ...
<summary> Computation type: Computations which produce a stream of data in addition to the computed values. <para /> Binding strategy: Combines the outputs of the subcomputations using <c>mappend</c>. <para /> Useful for: Logging, or other computations that produce output "on the side". </summary>
val tell: w: 'Monoid -> Writer<'Monoid,unit>
<summary> Embeds a simple writer action. </summary>
type LogEntry = { msg: string } static member create: x: string -> LogEntry
static member LogEntry.create: x: string -> LogEntry
val calc: Writer<LogEntry list,int>
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 y: int
val sum: x: 'Foldable<'Monoid> -> 'Monoid (requires member Fold and member ``+`` and member Zero)
<summary> Folds the sum of all monoid elements in the Foldable. </summary>
<category index="23">Additional Functions</category>
val logs: LogEntry list
val exec: Writer<'Monoid,'T> -> 'Monoid
<summary> Extract the output from a writer computation. </summary>
val logs': LogEntry list
val run: Writer<'Monoid,'T> -> 'T * 'Monoid
<summary> Unwraps a writer computation as a (result, output) pair. (The inverse of Writer.) </summary>
val output': x: string -> Writer<DList<LogEntry>,unit>
Multiple items
module DList from FSharpPlus.Data

type DList<'T> = interface IReadOnlyList<'T> interface IEquatable<DList<'T>> new: length: int * data: DListData<'T> -> DList<'T> member Add: x: 'T -> DList<'T> member Cons: hd: 'T -> DList<'T> override Equals: other: obj -> bool override GetHashCode: unit -> int member toSeq: unit -> IEnumerator<'T> static member (+) : x: DList<'a> * y: DList<'a> -> DList<'a> static member (<*>) : f: DList<('a -> 'b)> * x: DList<'a> -> DList<'b> ...
<summary> DList is an ordered linear structure implementing the List signature (head, tail, cons), end-insertion (add), and O(1) append. Ordering is by insertion history. DList is an implementation of [John Hughes' append list](http://dl.acm.org/citation.cfm?id=8475). </summary>

new: length: int * data: DListData<'T> -> DList<'T>
Multiple items
val ofSeq: s: 'T seq -> DList<'T>
<summary> Returns a DList of the seq. </summary>

static member DList.ofSeq: s: 'T seq -> DList<'T>
val calc': Writer<DList<LogEntry>,int>
val logs2: DList<LogEntry>
val y': int
val logs2': DList<LogEntry>