Resumable Monad


The persisted multistep resumable monad

This module implements the persisted & multistep implementation of the resumable monad. Multistep means that the where the resumable expression is encoded as a mapping from the trace history type 'h to the expression's return type 't as opposed to a single step semantics ('h -> 'h * Option<'t>). Persisted means that the trace of caching points is persisted to some external storage device as the computation progresses. If the computation is interrupted for any external reason (e.g., machine shuts down, process killed, machine upgraded, ...) then the computation can be resumed from the persisted stated and resume from the last evaluated caching point.

  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: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
110: 
111: 
112: 
113: 
114: 
115: 
116: 
117: 
118: 
119: 
120: 
121: 
122: 
123: 
124: 
125: 
126: 
127: 
128: 
129: 
130: 
131: 
132: 
133: 
134: 
135: 
136: 
137: 
138: 
139: 
140: 
141: 
142: 
143: 
144: 
145: 
146: 
147: 
148: 
149: 
150: 
151: 
152: 
/// Persisted implementation of the multistep resumable monad.
module ResumableMonad.MultipstepPersisted

#if INTERACTIVE
#load "zero.fs"
#load "Scripts\load-references-debug.fsx"
#endif


/// Defines a method to persist a trace history (i.e. caching points) to some external device
type Persist<'h> = 'h -> unit

/// Defines the functions to load/save the caching points to some external storage
type CacheStorage<'h> =
    {
        save : Persist<'h>
        load : unit -> 'h option
    }

/// A resumable computation of type `'t` with caching points history of type `'h`.
/// - `'t` is the returned type of the computation
/// - type `'h` is inferred from the monadic expression and encodes the history of caching points in the
///  resumable expression. The hierachy of caching points is encoded with nested tuples, the leaf elements in the
///  hiearachy are of type `'a option` and represent the caching points themselves.
type Resumable<'h,'t> = Resumable of (Persist<'h> -> 'h -> 't)
with
    /// Resume from a specified history of caching points
    member inline R.resume (persist:Persist<'h>) (h:'h) =
        let (Resumable r) = R
        r persist h

    /// Returns the empty history (i.e. no caching point)
    member inline __.initial =
        Zero.getZeroTyped<'h>

    /// Evaluate the resumable expression starting
    /// from the saved history of cached points if it exists,
    /// or from the empty history otherwise
    member inline R.evaluate (storage:CacheStorage<'h>) =
        let (Resumable resume) = R
        printfn "State type is %O" typeof<'h>
        let state =
            match storage.load() with
            | None ->
                printfn "No cached state in storage: starting from initial state"
                R.initial
            | Some state ->
                printfn "Resuming from existing cached state"
                state

        resume storage.save state

/// A resumable computation of type `'t` with no caching point.
/// This extra type is used as a trick to match
/// on type `'h` at compile-type using .net member overloading
/// (since unfortunatley the static constraint `not ('h :> unit)` cannot be expressed in F#).
//
/// It's not theoretically needed but it helps simplify
/// the type encoding `'h` of caching points by eliminating
/// redundant occurrences of `option unit` within
/// larger resumable expressions.
and Resumable<'t> = Startable of (unit -> 't)
with
    member inline R.resume () =
        let (Startable r) = R
        r ()

/// Return the provided value if specified otherwise evaluate the provided function
/// and persist the result to the external storage device
let inline getOrEvaluate (persist:Persist<'a>) (evaluate: unit -> 'a) = function
    | Some cached ->
        printfn "Reusing cached value: %O" cached
        cached
    | None ->
        printfn "Cache miss: evaluating resumable expression..."
        let r = evaluate()
        printfn "Persisting result to cache: %O" r
        persist r
        r

/// Return a persist function that applies a transformation and then falls back to the specified persist function
let inline (+~) (persist:Persist<'h>) (cons:'a->'h) :Persist<'a> = cons >> persist

/// The syntax builder for the Resumable monadic syntax
type ResumableBuilder() =

    member __.Zero<'t>() : Resumable<_> =
        Startable <| fun () -> ()

    member __.Return(x:'t) =
        Startable <| fun () -> x

    member __.Delay(f: unit -> Resumable<'h,'t>) =
        printfn "===Delay"
        Resumable <| (fun (p:Persist<'h>) h -> f().resume p h)

    member __.Delay(f: unit -> Resumable<'t>) =
        Startable <| fun () -> f().resume ()

    // Resumable<'u,'a> -> ('a->Resumable<'v, 'b>) -> Resumable<'a option * 'u * 'v, 'b>
    member inline __.Bind(f:Resumable<'u,'a>, g:'a->Resumable<'v, 'b>) =
        printfn "===1: 'u:%O 'a: %O 'v: %O 'b: %O" typeof<'u> typeof<'a> typeof<'v> typeof<'b>
        Resumable (fun (p:Persist<'a option * 'u * 'v>) (cached:'a option, u:'u, v:'v) ->
                let persista = p +~ fun a -> Some a, u, v
                let persistu = p +~ fun u -> None, u, v
                let a = cached |> getOrEvaluate persista (fun () -> f.resume persistu u)
                (g a).resume (p +~ fun v -> Some a, u, v) v)
  
    // Resumable<'u,'a> -> ('a->Resumable<'b>) -> Resumable<'a option * 'u, 'b>
    member inline __.Bind(f:Resumable<'u,'a>, g:'a->Resumable<'b>) =
        printfn "===2: 'u: %O 'a: %O 'b:%O" typeof<'u> typeof<'a> typeof<'b>
        Resumable (fun (p:Persist<'a option * 'u>) (cached, u) ->
                        let persista = p +~ fun a -> Some a, u
                        let persistu = p +~ fun u -> None, u
                        (cached |> getOrEvaluate persista (fun () -> f.resume persistu u) |> g).resume())

    // Resumable<'a> -> ('a->Resumable<'v, 'b>) -> Resumable<'a option * 'v, 'b> =
    member inline __.Bind(f:Resumable<'a>, g:'a->Resumable<'v, 'b>) =
        printfn "===3: 'a: %O 'v: %O 'b:%O" typeof<'a> typeof<'v> typeof<'b>
        Resumable (fun (p:Persist<'a option * 'v>) (cached:'a option, v) -> 
                        let a :'a = cached |> getOrEvaluate (p +~ fun a -> Some a, v) f.resume
                        (g a).resume (p +~ fun v -> Some a, v) v)

    // Resumable<'a> -> ('a->Resumable<'b>) -> Resumable<'a option, 'b> =
    member inline __.Bind(f:Resumable<'a>, g:'a->Resumable<'b>) =
        printfn "===4: 'a: %O 'b: %O" typeof<'a> typeof<'b>
        Resumable (fun (p:Persist<'a option>) cached ->
                    let a = cached |> getOrEvaluate (p +~ Some) f.resume
                    (g a).resume())

    // Resumable<'a> -> ('a->Resumable<'b>) -> Resumable<'b>
    member inline __.BindNoCache(f:Resumable<'a>, g:'a->Resumable<'b>) =
        printfn "===5: 'a: %O 'b: %O" typeof<'a> typeof<'b>
        Startable (fun () -> (g <| f.resume()).resume())

    // Resumable<'u,unit> -> Resumable<'v,'b> -> Resumable<'u * 'v,'b>
    member inline __.Combine(p1:Resumable<'u,unit>, p2:Resumable<'v,'b>) =
        printfn "===6"
        Resumable (fun (p:Persist<'u * 'v>) (u, v) ->
                                p1.resume (p +~ fun u -> u, v) u
                                p2.resume (p +~ fun v -> u, v) v)

    // Resumable<unit> -> Resumable<'b> -> Resumable<'b>
    member inline __.Combine(p1:Resumable<unit>, p2:Resumable<'b>) =
        printfn "===7"
        Startable (fun () -> p1.resume(); p2.resume())

    member __.While(condition, body:Resumable<unit>) : Resumable<unit> =
        if condition() then
            __.BindNoCache(body, (fun () -> __.While(condition, body)))
        else
            __.Zero()

We now define the computational expression resumable { ... } with all the syntactic sugar automatically inferred from the above monadic operators.

 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: 
let resumable = ResumableBuilder()

/// File storage based on Newtonsoft serialization
let inline NewtonsoftStorage< ^T> fileName =
    {
        save = fun history -> System.IO.File.WriteAllText(fileName, Newtonsoft.Json.JsonConvert.SerializeObject(history))
        load = fun () ->
                if System.IO.File.Exists fileName then
                    Some <| Newtonsoft.Json.JsonConvert.DeserializeObject< ^T>(System.IO.File.ReadAllText(fileName))
                else
                    None
    }

/// File storage based on FSharpLu.Json
let inline LuStorage< ^T> fileName =
    {
        save = Microsoft.FSharpLu.Json.Compact.serializeToFile fileName
        load = fun () ->
                    if System.IO.File.Exists fileName then
                        printfn "Loading cached points from file %s" fileName
                        let cache = Microsoft.FSharpLu.Json.Compact.tryDeserializeFile< ^T> fileName
                        match cache with
                        | Choice1Of2 cache -> Some cache
                        | Choice2Of2 _ -> None

                    else
                        printfn "Cache history file not found, starting computation from scratch."
                        None
    }

A simple example: finding three large prime numbers and summing them up

 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: 
module Example =
    open System

    let isPrime = function
    | 1 -> false
    | 2 -> true
    | n -> { 2..(int <| Math.Ceiling(Math.Sqrt(float n))) } |> Seq.forall (fun d -> n % d <> 0)

    let nthprime n =
        printfn "Calculating %dth prime..." n
        let p = Seq.initInfinite id |> Seq.where isPrime |> Seq.item n
        printfn "%dth prime is: %d" n p
        p

    let addPrimes n1 n2 n3 =
        resumable {
            printfn "Starting computation"

            let! p1 = resumable { return nthprime n1 }

            printfn "Press enter to continue or CTRL+BREAK to pause the calculation here."
            System.Console.Read() |> ignore

            let! p2 = resumable { return nthprime n2 }

            printfn "Press enter to continue or CTRL+BREAK to pause the calculation here."
            System.Console.Read()|> ignore

            let! p3 = resumable { return nthprime n3 }

            let sum = p1 + p2 + p3
            printfn "The sum of %dth prime, %dth prime and %dth prime is %d"  n1 n2 n3 sum
            return sum
        }

    let f = System.IO.Path.GetTempFileName()
    printfn "State file is %s" f

    System.IO.File.Delete(f)
    let result = (addPrimes 30000 20000 10000).evaluate (LuStorage f)
    printfn "Result of the resumable computation is %d" result

    printf "%s" <| System.IO.File.ReadAllText(f)

    
namespace ResumableMonad
module MultipstepPersisted

from ResumableMonad


 Persisted implementation of the multistep resumable monad.
type Persist<'h> = 'h -> unit

Full name: ResumableMonad.MultipstepPersisted.Persist<_>


 Defines a method to persist a trace history (i.e. caching points) to some external device
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
type CacheStorage<'h> =
  {save: Persist<'h>;
   load: unit -> 'h option;}

Full name: ResumableMonad.MultipstepPersisted.CacheStorage<_>


 Defines the functions to load/save the caching points to some external storage
CacheStorage.save: Persist<'h>
CacheStorage.load: unit -> 'h option
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
Multiple items
union case Resumable.Resumable: (Persist<'h> -> 'h -> 't) -> Resumable<'h,'t>

--------------------
type Resumable<'t> =
  | Startable of (unit -> 't)
  member resume : unit -> 't

Full name: ResumableMonad.MultipstepPersisted.Resumable<_>


 A resumable computation of type `'t` with no caching point.
 This extra type is used as a trick to match
 on type `'h` at compile-type using .net member overloading
 (since unfortunatley the static constraint `not ('h :> unit)` cannot be expressed in F#).
 It's not theoretically needed but it helps simplify
 the type encoding `'h` of caching points by eliminating
 redundant occurrences of `option unit` within
 larger resumable expressions.


--------------------
type Resumable<'h,'t> =
  | Resumable of (Persist<'h> -> 'h -> 't)
  member evaluate : storage:CacheStorage<'h> -> 't
  member initial : 'h
  member resume : persist:Persist<'h> -> h:'h -> 't

Full name: ResumableMonad.MultipstepPersisted.Resumable<_,_>


 A resumable computation of type `'t` with caching points history of type `'h`.
 - `'t` is the returned type of the computation
 - type `'h` is inferred from the monadic expression and encodes the history of caching points in the
  resumable expression. The hierachy of caching points is encoded with nested tuples, the leaf elements in the
  hiearachy are of type `'a option` and represent the caching points themselves.
val R : Resumable<'h,'t>
member Resumable.resume : persist:Persist<'h> -> h:'h -> 't

Full name: ResumableMonad.MultipstepPersisted.Resumable`2.resume


 Resume from a specified history of caching points
val persist : Persist<'h>
val h : 'h
val r : (Persist<'h> -> 'h -> 't)
member Resumable.initial : 'h

Full name: ResumableMonad.MultipstepPersisted.Resumable`2.initial


 Returns the empty history (i.e. no caching point)
member Resumable.evaluate : storage:CacheStorage<'h> -> 't

Full name: ResumableMonad.MultipstepPersisted.Resumable`2.evaluate


 Evaluate the resumable expression starting
 from the saved history of cached points if it exists,
 or from the empty history otherwise
val storage : CacheStorage<'h>
val resume : (Persist<'h> -> 'h -> 't)
val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val typeof<'T> : System.Type

Full name: Microsoft.FSharp.Core.Operators.typeof
val state : 'h
union case Option.None: Option<'T>
property Resumable.initial: 'h


 Returns the empty history (i.e. no caching point)
union case Option.Some: Value: 'T -> Option<'T>
union case Resumable.Startable: (unit -> 't) -> Resumable<'t>
val R : Resumable<'t>
member Resumable.resume : unit -> 't

Full name: ResumableMonad.MultipstepPersisted.Resumable`1.resume
val r : (unit -> 't)
val getOrEvaluate : persist:Persist<'a> -> evaluate:(unit -> 'a) -> _arg1:'a option -> 'a

Full name: ResumableMonad.MultipstepPersisted.getOrEvaluate


 Return the provided value if specified otherwise evaluate the provided function
 and persist the result to the external storage device
val persist : Persist<'a>
val evaluate : (unit -> 'a)
val cached : 'a
val r : 'a
val cons : ('a -> 'h)
Multiple items
type ResumableBuilder =
  new : unit -> ResumableBuilder
  member Bind : f:Resumable<'u,'a> * g:('a -> Resumable<'v,'b>) -> Resumable<('a option * 'u * 'v),'b>
  member Bind : f:Resumable<'u,'a> * g:('a -> Resumable<'b>) -> Resumable<('a option * 'u),'b>
  member Bind : f:Resumable<'a> * g:('a -> Resumable<'v,'b>) -> Resumable<('a option * 'v),'b>
  member Bind : f:Resumable<'a> * g:('a -> Resumable<'b>) -> Resumable<'a option,'b>
  member BindNoCache : f:Resumable<'a> * g:('a -> Resumable<'b>) -> Resumable<'b>
  member Combine : p1:Resumable<'u,unit> * p2:Resumable<'v,'b> -> Resumable<('u * 'v),'b>
  member Combine : p1:Resumable<unit> * p2:Resumable<'b> -> Resumable<'b>
  member Delay : f:(unit -> Resumable<'h,'t>) -> Resumable<'h,'t>
  member Delay : f:(unit -> Resumable<'t>) -> Resumable<'t>
  ...

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder


 The syntax builder for the Resumable monadic syntax


--------------------
new : unit -> ResumableBuilder
member ResumableBuilder.Zero : unit -> Resumable<unit>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Zero
val __ : ResumableBuilder
member ResumableBuilder.Return : x:'t -> Resumable<'t>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Return
val x : 't
member ResumableBuilder.Delay : f:(unit -> Resumable<'h,'t>) -> Resumable<'h,'t>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Delay
val f : (unit -> Resumable<'h,'t>)
val p : Persist<'h>
member ResumableBuilder.Delay : f:(unit -> Resumable<'t>) -> Resumable<'t>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Delay
val f : (unit -> Resumable<'t>)
member ResumableBuilder.Bind : f:Resumable<'u,'a> * g:('a -> Resumable<'v,'b>) -> Resumable<('a option * 'u * 'v),'b>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Bind
val f : Resumable<'u,'a>
val g : ('a -> Resumable<'v,'b>)
val p : Persist<'a option * 'u * 'v>
val cached : 'a option
val u : 'u
val v : 'v
val persista : Persist<'a>
val a : 'a
val persistu : Persist<'u>
member Resumable.resume : persist:Persist<'h> -> h:'h -> 't


 Resume from a specified history of caching points
member ResumableBuilder.Bind : f:Resumable<'u,'a> * g:('a -> Resumable<'b>) -> Resumable<('a option * 'u),'b>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Bind
val g : ('a -> Resumable<'b>)
val p : Persist<'a option * 'u>
member ResumableBuilder.Bind : f:Resumable<'a> * g:('a -> Resumable<'v,'b>) -> Resumable<('a option * 'v),'b>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Bind
val f : Resumable<'a>
val p : Persist<'a option * 'v>
member Resumable.resume : unit -> 't
member ResumableBuilder.Bind : f:Resumable<'a> * g:('a -> Resumable<'b>) -> Resumable<'a option,'b>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Bind
val p : Persist<'a option>
member ResumableBuilder.BindNoCache : f:Resumable<'a> * g:('a -> Resumable<'b>) -> Resumable<'b>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.BindNoCache
member ResumableBuilder.Combine : p1:Resumable<'u,unit> * p2:Resumable<'v,'b> -> Resumable<('u * 'v),'b>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Combine
val p1 : Resumable<'u,unit>
val p2 : Resumable<'v,'b>
val p : Persist<'u * 'v>
member ResumableBuilder.Combine : p1:Resumable<unit> * p2:Resumable<'b> -> Resumable<'b>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.Combine
val p1 : Resumable<unit>
val p2 : Resumable<'b>
member ResumableBuilder.While : condition:(unit -> bool) * body:Resumable<unit> -> Resumable<unit>

Full name: ResumableMonad.MultipstepPersisted.ResumableBuilder.While
val condition : (unit -> bool)
val body : Resumable<unit>
member ResumableBuilder.BindNoCache : f:Resumable<'a> * g:('a -> Resumable<'b>) -> Resumable<'b>
member ResumableBuilder.While : condition:(unit -> bool) * body:Resumable<unit> -> Resumable<unit>
member ResumableBuilder.Zero : unit -> Resumable<unit>
val resumable : ResumableBuilder

Full name: ResumableMonad.MultipstepPersisted.resumable
val NewtonsoftStorage<'T> : fileName:string -> CacheStorage<obj>

Full name: ResumableMonad.MultipstepPersisted.NewtonsoftStorage


 File storage based on Newtonsoft serialization
val fileName : string
val history : obj
namespace System
namespace System.IO
type File =
  static member AppendAllLines : path:string * contents:IEnumerable<string> -> unit + 1 overload
  static member AppendAllText : path:string * contents:string -> unit + 1 overload
  static member AppendText : path:string -> StreamWriter
  static member Copy : sourceFileName:string * destFileName:string -> unit + 1 overload
  static member Create : path:string -> FileStream + 3 overloads
  static member CreateText : path:string -> StreamWriter
  static member Decrypt : path:string -> unit
  static member Delete : path:string -> unit
  static member Encrypt : path:string -> unit
  static member Exists : path:string -> bool
  ...

Full name: System.IO.File
System.IO.File.WriteAllText(path: string, contents: string) : unit
System.IO.File.WriteAllText(path: string, contents: string, encoding: System.Text.Encoding) : unit
System.IO.File.Exists(path: string) : bool
System.IO.File.ReadAllText(path: string) : string
System.IO.File.ReadAllText(path: string, encoding: System.Text.Encoding) : string
val LuStorage<'T> : fileName:string -> CacheStorage<int option * (int option * int option)>

Full name: ResumableMonad.MultipstepPersisted.LuStorage


 File storage based on FSharpLu.Json
namespace Microsoft
val cache : Choice<(int option * (int option * int option)),obj>
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
val cache : int option * (int option * int option)
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
module Example

from ResumableMonad.MultipstepPersisted
val isPrime : _arg1:int -> bool

Full name: ResumableMonad.MultipstepPersisted.Example.isPrime
val n : 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 Math =
  static val PI : float
  static val E : float
  static member Abs : value:sbyte -> sbyte + 6 overloads
  static member Acos : d:float -> float
  static member Asin : d:float -> float
  static member Atan : d:float -> float
  static member Atan2 : y:float * x:float -> float
  static member BigMul : a:int * b:int -> int64
  static member Ceiling : d:decimal -> decimal + 1 overload
  static member Cos : d:float -> float
  ...

Full name: System.Math
Math.Ceiling(a: float) : float
Math.Ceiling(d: decimal) : decimal
Math.Sqrt(d: float) : float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

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

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
module Seq

from Microsoft.FSharp.Collections
val forall : predicate:('T -> bool) -> source:seq<'T> -> bool

Full name: Microsoft.FSharp.Collections.Seq.forall
val d : int
val nthprime : n:int -> int

Full name: ResumableMonad.MultipstepPersisted.Example.nthprime
val p : int
val initInfinite : initializer:(int -> 'T) -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.initInfinite
val id : x:'T -> 'T

Full name: Microsoft.FSharp.Core.Operators.id
val where : predicate:('T -> bool) -> source:seq<'T> -> seq<'T>

Full name: Microsoft.FSharp.Collections.Seq.where
val item : index:int -> source:seq<'T> -> 'T

Full name: Microsoft.FSharp.Collections.Seq.item
val addPrimes : n1:int -> n2:int -> n3:int -> Resumable<(int option * (int option * int option)),int>

Full name: ResumableMonad.MultipstepPersisted.Example.addPrimes
val n1 : int
val n2 : int
val n3 : int
val p1 : int
type Console =
  static member BackgroundColor : ConsoleColor with get, set
  static member Beep : unit -> unit + 1 overload
  static member BufferHeight : int with get, set
  static member BufferWidth : int with get, set
  static member CapsLock : bool
  static member Clear : unit -> unit
  static member CursorLeft : int with get, set
  static member CursorSize : int with get, set
  static member CursorTop : int with get, set
  static member CursorVisible : bool with get, set
  ...

Full name: System.Console
Console.Read() : int
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
val p2 : int
val p3 : int
val sum : int
val f : string

Full name: ResumableMonad.MultipstepPersisted.Example.f
type Path =
  static val DirectorySeparatorChar : char
  static val AltDirectorySeparatorChar : char
  static val VolumeSeparatorChar : char
  static val InvalidPathChars : char[]
  static val PathSeparator : char
  static member ChangeExtension : path:string * extension:string -> string
  static member Combine : [<ParamArray>] paths:string[] -> string + 3 overloads
  static member GetDirectoryName : path:string -> string
  static member GetExtension : path:string -> string
  static member GetFileName : path:string -> string
  ...

Full name: System.IO.Path
IO.Path.GetTempFileName() : string
IO.File.Delete(path: string) : unit
val result : int

Full name: ResumableMonad.MultipstepPersisted.Example.result
val printf : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printf
IO.File.ReadAllText(path: string) : string
IO.File.ReadAllText(path: string, encoding: Text.Encoding) : string
Fork me on GitHub