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.
open FSharpPlus
open FSharpPlus.Data
type LogEntry={msg:string}
with
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<'a1> * y: DList<'a1> -> DList<'a1>
static member (<*>) : f: DList<('a1 -> 'a2)> * x: DList<'a1> -> DList<'a2>
...
<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>