5 people like it.
Like the snippet!
Alternative reader monad
This is a reader monad, whit the difference that multiple reads access different parts of the state. This is done by building up a tuple (in the bind operator) that represents different parts of the state that can be accessed by different parts of the computation.
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:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
|
/// A reader monad is a computation that takes a state 'S and produces a value 'T
///
/// In the usual reader monad, we would have `unit : T -> Reader<S, T>` and
/// `bind : (T1 -> Reader<S, T2>) -> Reader<S, T1> -> Reader<S, T2>` but
/// we're going to do things a bit differently here and the state we read
/// is going to be composed as a tuple of things that are read by the individual
/// computations composed by bind.
type Reader<'S, 'T> = Reader of ('S -> 'T)
/// The unit operation does not read any state and so it produces `Reader<unit, T>`
let unit v = Reader(fun () -> v)
/// The bind operation takes `Reader<S1, T1>` and a function that reads another
/// part of the state `T1 -> Reader<S2, T2>` and produces a reader that reads
/// both state values (in a tuple) so that's `Reader<S1*S2, T2>`
let bind f (Reader(g)) = Reader(fun (a, b) ->
let v = g a
let (Reader h) = f v
h b)
/// Defines the computation builder - add `Delay` so that we can delay things!
type ReaderBuilder() =
member x.Return(v) = unit v
member x.Bind(v, f) = bind f v
member x.Delay(f) = Reader (fun s ->
let (Reader h) = f ()
h s )
let reader = ReaderBuilder()
/// A function that takes a computation written as `Reader<S, T>`. It builds a new
/// computation of type `Reader<option<T>*S, T>` that attempts to read a cached result
/// of type T from the state - if it is there, we just return it, otherwise we run
/// the computation provided as an argument to actually compute the value.
let cache (Reader(f)) = (Reader(fun (v1, v2) ->
match v1 with
| None -> f v2
| Some v -> v))
/// Returns `n` and prints string to log what's happening
let read s n = reader {
printfn "Reading: %s" s
return n }
let m = reader {
// The following two are only run if the value is not already available
let! a = cache <| read "A" 1
let! b = cache <| read "B" 2
// The following read is done always (not accesses the state)
let! c = read "C" 3
return a + b + c
}
let (Reader f) = m
f ((None, ()), ((None, ()), ((), ())))
f ((Some 1, ()), ((None, ()), ((), ())))
f ((None, ()), ((Some 2, ()), ((), ())))
f ((Some 1, ()), ((Some 2, ()), ((), ())))
|
Multiple items
union case Reader.Reader: ('S -> 'T) -> Reader<'S,'T>
--------------------
type Reader<'S,'T> = | Reader of ('S -> 'T)
Full name: Script.Reader<_,_>
A reader monad is a computation that takes a state 'S and produces a value 'T
In the usual reader monad, we would have `unit : T -> Reader<S, T>` and
`bind : (T1 -> Reader<S, T2>) -> Reader<S, T1> -> Reader<S, T2>` but
we're going to do things a bit differently here and the state we read
is going to be composed as a tuple of things that are read by the individual
computations composed by bind.
Multiple items
val unit : v:'a -> Reader<unit,'a>
Full name: Script.unit
The unit operation does not read any state and so it produces `Reader<unit, T>`
--------------------
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
val v : 'a
val bind : f:('a -> Reader<'b,'c>) -> Reader<'d,'a> -> Reader<('d * 'b),'c>
Full name: Script.bind
The bind operation takes `Reader<S1, T1>` and a function that reads another
part of the state `T1 -> Reader<S2, T2>` and produces a reader that reads
both state values (in a tuple) so that's `Reader<S1*S2, T2>`
val f : ('a -> Reader<'b,'c>)
val g : ('d -> 'a)
val a : 'd
val b : 'b
val h : ('b -> 'c)
Multiple items
type ReaderBuilder =
new : unit -> ReaderBuilder
member Bind : v:Reader<'c,'d> * f:('d -> Reader<'e,'f>) -> Reader<('c * 'e),'f>
member Delay : f:(unit -> Reader<'a,'b>) -> Reader<'a,'b>
member Return : v:'g -> Reader<unit,'g>
Full name: Script.ReaderBuilder
Defines the computation builder - add `Delay` so that we can delay things!
--------------------
new : unit -> ReaderBuilder
val x : ReaderBuilder
member ReaderBuilder.Return : v:'g -> Reader<unit,'g>
Full name: Script.ReaderBuilder.Return
val v : 'g
member ReaderBuilder.Bind : v:Reader<'c,'d> * f:('d -> Reader<'e,'f>) -> Reader<('c * 'e),'f>
Full name: Script.ReaderBuilder.Bind
val v : Reader<'c,'d>
val f : ('d -> Reader<'e,'f>)
member ReaderBuilder.Delay : f:(unit -> Reader<'a,'b>) -> Reader<'a,'b>
Full name: Script.ReaderBuilder.Delay
val f : (unit -> Reader<'a,'b>)
val s : 'a
val h : ('a -> 'b)
val reader : ReaderBuilder
Full name: Script.reader
val cache : Reader<'a,'b> -> Reader<('b option * 'a),'b>
Full name: Script.cache
A function that takes a computation written as `Reader<S, T>`. It builds a new
computation of type `Reader<option<T>*S, T>` that attempts to read a cached result
of type T from the state - if it is there, we just return it, otherwise we run
the computation provided as an argument to actually compute the value.
val f : ('a -> 'b)
val v1 : 'b option
val v2 : 'a
union case Option.None: Option<'T>
union case Option.Some: Value: 'T -> Option<'T>
val v : 'b
val read : s:string -> n:'a -> Reader<unit,'a>
Full name: Script.read
Returns `n` and prints string to log what's happening
val s : string
val n : 'a
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val m : Reader<((int option * unit) * ((int option * unit) * (unit * unit))),int>
Full name: Script.m
val a : int
val b : int
val c : int
val f : ((int option * unit) * ((int option * unit) * (unit * unit)) -> int)
Full name: Script.f
More information