# Resumable monad, idempotence and cloud services

October 18th, 2015, by William Blum

## Abstract

We define a new monad and its associated F# syntactic sugar resumable { ... } to express computations that can be interrupted at specified control points and resumed in subsequent executions while carrying along state from the previous execution.

Resumable expresssions make it simpler to write idempotent and resumable code which is often necessary when writing cloud services code.

## Motivating example

Suppose that we are building a service that creates virtual machines. The first step is to obtain the name of the machine to be created. The second step is to defer the actual virtual machine provisioning to an actual cloud provider like Amazon or Azure through an asynchronous API call. The external provider returns a request ID used to check status of the request. The final step is to poll the request ID until the request succeeds or fails (e.g., the virtual machine is provisioned or an error occurred).

Let's first define helper functions to model this environment.

 1:  module Environment = 

First we need a function called by our service to retrieve the details of the request (such as machine name, type of the machine, ...) For simplicity here we will assume it's just a machine name.

 1: 2: 3: 4:   let getMachineName () = printfn "Enter name of machine: " let machineName = System.Console.ReadLine() machineName 

Now let's define a simple model of the cloud service API used to provision new virtual machines. The function below is just a mockup for the real API: it returns a random number representing the request ID created by the cloud VM provider.

 1: 2: 3: 4: 5:   let provisionVM machineName = let requestId = System.Random().Next() printfn "Provisioning new VM %s in the cloud...." machineName printfn "Request ID returned from API is: %d" requestId requestId 

Last we model the cloud API that checks the status of a previously issued request. To simulate the processing time normally required for such operation to complete we count how many times the function was called and after 5 attempts we return 'success'.

 1: 2: 3: 4: 5: 6: 7:   let vmRequestSucceeded = let waitTime = ref 0 let rec aux requestId = printfn "checking status of request %d (waitTime: %d)" requestId !waitTime waitTime := (!waitTime + 1) % 5 !waitTime = 0 aux 

Now that our environment is defined we can implement our service operation operationally as follows.

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:  module MyService = let myServiceApi () = let machineName = Environment.getMachineName () let requestId = Environment.provisionVM machineName while not <| Environment.vmRequestSucceeded requestId do printfn "Waiting for cloud request to complete..." System.Threading.Thread.Sleep(1000) printfn "Request completed for machine %s!" machineName machineName, requestId 

The logic is straightforward: we get the machine name from the client who made the request, we then forward the request to the cloud and then poll until the cloud request completes.

This works very well except that, typically, service running in the cloud (e.g., as a worker role or a micro service) can be interrupted at any moment. The machine hosting the service can be restarted, upgraded, or rescaled. In the example above the operation can be interrupted at any point. Suppose for instance that it is stopped right after sending the VM provisioning request to the cloud. What happens next? Typically the infrastructure on which we run our service will detect failure to complete the request after a certain timeout and will schedule a new request. At some point this new request will be picked up by another instance of our service. When this happens we want the operation to resume where it left off instead of restarting from scratch. If we don't handle this situation we may end up with an orphan virtual machine, having to pay for two virtual machines instead of one!

To achieve this we need a mechanism to define resumable control points in our implementation. There should be one resumable point each time an important unit of work is completed. The set of resumable points implicitly defines a global progress state for our operation. Such state can be saved somewhere (for instance on a cloud blob or queue) so that when our function is called again it can read the state and starts where it left off.

Such code transformation can be done manually but it's a tedious error-prone task: it basically boils down to converting your code into a state machine. This requires you to first identify the resumable points in your code, identify the intermediate states at each resumable points, and implement some state serialization/deserialization logic.

What if all this boiler plate code could be generated automatically? What if we could just implement our service operation with almost identical code as above and have the state machine logic generated for us?

Here is how we would like to write it:

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:  module MyResumableService = let myServiceApi = resumable { let! machineName = resumable { return Environment.getMachineName () } let! requestId = resumable { return Environment.provisionVM machineName } let! vmready = resumable { while not <| Environment.vmRequestSucceeded requestId do printfn "Waiting for cloud request to complete..." System.Threading.Thread.Sleep(1000) return true } printfn "Request completed for machine %s!" machineName return machineName, requestId, vmready } 

# Resumable computational expression

We need a data type to encode the state of the computation. The state will be the sequence of all results returned at each resumable control point in the computation. We thus introduce the type Cell<'t> to hold the result of type 't returned at one individual resumable control point:

 1: 2: 3:  type Cell<'t> = | NotExecuted | Result of 't 

There is one cell for each resumable control point in the computation. Before the corresponding operation is executed the cell contains NotExecuted, once it has been executed the cell holds the result yielded at the control point.

A resumable control point can then be encoded as a function taking the current trace of type 'h as parameter and returning the new trace together with the new value produced at this control point:

 1:  type Resumable<'h,'t> = 'h -> 'h * Cell<'t> 

Finally here comes the meaty part: the definition of the monadic operators. My monadic skills being rather rusty it took me a weekend to figure out the implementation details... The result is deceptively simple:

  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:  type ResumableBuilder() = member __.Zero() = fun () -> (), (Result ()) member __.Return(x:'t) = fun () -> (), (Result x) member __.ReturnFrom(x) = x member __.Delay(generator:unit->Resumable<'u,'a>) = fun h -> generator() h member __.Bind( f:Resumable<'u,'a>, g:'a->Resumable<'v, 'b> ) : Resumable<'u * Cell<'a> * 'v, 'b> = fun (u: 'u, a : Cell<'a>, v : 'v) -> match a with | NotExecuted -> // The result of f is misssing, we thus // advance f's computation by one step let u_stepped, a_stepped = f u (u_stepped, a_stepped, v), NotExecuted | Result _a -> // Since f's computation has finished // we advance g's computation by one step. let v_stepped, b_stepped = g _a v (u, a, v_stepped), b_stepped member __.Combine(p1:Resumable<'u,unit>,p2:Resumable<'v,'b>) :Resumable<'u*Cell*'v,'b>= __.Bind(p1, (fun () -> p2)) member __.While(gd, prog:Resumable) : Resumable = let rec whileA gd prog = if not <| gd() then __.Zero() else fun u -> prog u whileA gd prog 

The idea in the above definition is to decompose a trace of execution as a triple of the form (u,a,v):

• The u part represents a prefix of the trace.
• The a part represents the cell in context.
• The v part represents the suffix of the trace, that is the result returned at each resumable control point following the cell in context.

I've tried several other encodings before I came up with this one. It may seem more obvious for instance to encode traces with pairs of the form (prefix,last) where prefix is the list of past executed operations and last represents the last operation to execute. Unfortunately defining monadic operators based on such encodings becomes an insurmountable task. The triple decomposition turns out to be necessary to deal with compositionality in the implementation of the Bind monadic operator.

A much better encoding would be to define the trace as a heterogeneous list of elements but the fairly limited type system provided by F# (compared to Haskell for instance) prevents you from doing that. You would have to give up type safety and define your trace as an array of obj and appeal to .Net reflection to make this all work. Of course I am not willing to give up type safety so I did not even go there...

We can now define syntactic sugar in F# for the monadic operators above defined:

 1:  let resumable = new ResumableBuilder() 

Let's make use of it on a simple example first: a computation that creates the pair (1,2) in two resumable steps:

 1: 2: 3: 4: 5: 6:  module Example = let example = resumable { let! x = resumable { return 1 } let! y = resumable { return 2 } return x, y } 

The type returned by the resumable construct is a function taking the current state of the trace as a parameter and returns both the new trace and the overall return value. In order to call this function we need to pass the inital value of the trace as a parameter (we will get back to this later in more details).

 1: 2: 3: 4:   let s0 = ((), NotExecuted, ((), NotExecuted, ())) let s1, r1 = example s0 let s2, r2 = example s1 let s3, r3 = example s2 

Each call to example advance the computation by one step. The first call produces 1, the second call produces 2 while the last call returns the expect result of Result(1,2). At each step the current trace state is returned and can be serialized to some external storage. If the computer running our code fails or restarts the state can be deserialized and resumed where it left off.

## Generating the initial trace state

This works all very well but in practice to actually execute a resumable expression you need to construct the initial state (or in monadic parlance the zero value of the type) yourself. One thing to notice is that the type of the trace is automatically generated by the monadic syntax. In the example above it is:

 1: 2: 3:  val example : (unit * Cell * (unit * Cell * unit) -> (unit * Cell * (unit * Cell * unit)) * Cell) 

The initial value for this trace type is therefore

 1:   let z0 = ((), NotExecuted, ((), NotExecuted, ())) 

The challenge is that this type gets bigger as the number of resumable points increases in your resumable expression, and so does the F# expression encoding the initial state value. The more control points you have in your computation the more complex the overall type gets.

So we need a better way to generate the initial value of the trace type for larger computations. How can we do that? One way is to define a helper function with a phantom type 'v:

 1:   let z<'a,'v> (r:'v) = ((),(NotExecuted:Cell<'a>),r) 

We can then equivalently define z0 as:

 1:   let z0_b = z << z <| () 

We can simplify this further: since the value z0 is passed as a parameter to the resumable computation later in the program the type of each cell is automatically inferred therefore we can omit all type parameters:

 1: 2:   let z0_c = z<_,_> << z<_,_> <| () example z0_c 

In fact F# let's you just write:

 1: 2:   let z0_d = z << z <| () example z0_d 

More generally, if your computation as n resumable control points the initial trace value is defined as above with instead n repetitions of the operator z.

OK, that's pretty neat but you still need to manually count how many resumable points you have in your expression to be able to automatically generate the initial trace value. Worse: the trick actually stops working when dealing with nested resumable expressions!

## Generating the initial trace through reflection

I've spent another weekend battling with the F# type system looking for a type-safe method to define the initial trace value, to no avail. It's once again limitations of the F# type system that makes it impossible to define the terminal element of the trace type in a type-safe manner. The core of the issue boils down to the impossibility of pattern matching on types in F#. Instead the solution I came up with involves arcane incantations to the .Net and F# type reflection APIs.

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:  module Zero = open Microsoft.FSharp.Reflection let rec getZeroUntyped<'X> (_type:System.Type) = if _type = typeof then box () elif _type.IsGenericType && _type.GetGenericTypeDefinition() = typedefof> then FSharpValue.MakeUnion (FSharpType.GetUnionCases(_type).[0], [||]) elif _type.IsGenericType && _type.GetGenericTypeDefinition() = typedefof< _*_*_ > then FSharpValue.MakeTuple (_type.GenericTypeArguments |> Array.map getZeroUntyped, _type) else invalidArg "_type" ("The provided type is not of valid form. The zero element can only be " + "calculated for trace types (those are types used to encode the state " + "of a resumable computation.)") let getZeroTyped<'X> = typeof<'X> |> getZeroUntyped |> unbox<'X> 

Here are some examples of use:

 1: 2: 3: 4: 5: 6: 7: 8: 9:   module Tests = getZeroTyped getZeroTyped > getZeroUntyped typeof * unit> let y =typeof * unit> getZeroUntyped y.GenericTypeArguments.[1] getZeroTyped * (unit * Cell * ((unit * Cell * unit) * Cell * unit))> 

Now that we have the helpers defined we can define the initial trace for the above example as follows

 1:  let _ = example (getZeroTyped<_>) 

The phantom type parameter of getZeroType is automatically inferred and the zero value automatically generated.

Now let's get back to our motivating example for provisioning virtual machines in the cloud:

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:  module MyResumableService = let myServiceApi = resumable { let! machineName = resumable { return Environment.getMachineName () } let! requestId = resumable { return Environment.provisionVM machineName } let! vmready = resumable { while not <| Environment.vmRequestSucceeded requestId do printfn "Waiting for cloud request to complete..." System.Threading.Thread.Sleep(1000) return true } printfn "Request completed for machine %s!" machineName return machineName, requestId, vmready } 

Does this look familiar? The actual implementation is almost identical to the original non-resumable one. We've just sprinkled the resumable keyword in strategic places where we expect the computation to be interrupted. If you have played with F# asynchronous workflows before this concept should be very familiar.

To run the operation we can just execute each individual step as follows:

 1: 2: 3: 4: 5:   let s1,r1 = myServiceApi (Zero.getZeroTyped<_>) // Get machine name let s2,r2 = myServiceApi s1 // Submit request and get request id let s3,r3 = myServiceApi s2 // Poll machine until ready let s4,r4 = myServiceApi s3 // Polling completed let s5,r5 = myServiceApi s4 // return result 

Each call returns a new state holding the current trace of execution. Executing each step of the computation one at a time is tedious and not very practical. Instead of manually calling the function to advance by a single step we can use the following helper to run the entire operation through completion:

 1: 2: 3: 4: 5: 6: 7: 8:  let rec runThroughCompletion m (state, result) = printfn "state: %A result: %A" state result match result with | NotExecuted -> runThroughCompletion m (m state) | Result r -> r let run m = runThroughCompletion m (Zero.getZeroTyped<_>, NotExecuted) 

Our service API can then be called in a single command:

 1:  run MyResumableService.myServiceApi 

## Idempotence

One important property of runThroughCompletion is that it is idempotent. This means that executing it once, twice or more times consecutively yields the exact same result. Mathematically this can be expressed as:

 1:   runThroughCompletion(runThroughCompletion(x)) = runThroughCompletion(x) 

for all state x.

This is a desirable property for services which can be interrupted for various reasons like outages, updates, scaling or migrations. When such event happens the service typically fails over to a redudant instance of the service that will attempt to execute the same operation again. Making the operation idempotent guarantees that the second execution yields a deterministic outcome regardless of whether the first execution completed or not.

## Cancellability and Resumability

Another desirable property is cancellability. Meaning that if the system interrupts the operation it should leave itself in a well-defined resumable state. In other words it should be possible to interrupt execution of runThroughCompletion without bringing the system to a corrupt state.

Note Here I want to highlight an important limitation of the resumable monad: it guarantees cancellability at resumable points only. If the execution is interrupted between those resumable points (for instance after the call to the virtual machine provisioning API but before the call returns with the requestId) then the state of the system will be undefined!

To achieve resumability one needs to identify the possible side-effects that the operation has on the environment. In our example those side-effects are:

1. A machine name was requested from the user;
2. A request to provision a VM was issued to an external cloud service;
3. The polling has completed and the cloud service has honoured the request.

The resumable monad let's you easily capture those side effects and control points using the resumable { ... } construct.

## Execution engine

The other thing we need is an execution engine that takes advantage of those control points to cancel and resume the computation. We can achieve that by adding persistence to our implementation of run. Each time we advance the computation to a resumable point we can save the current trace of execution and persist it to external storage (blob, queue,...). Next time we run the operation we read the saved state from disk and continue were we left off.

Here is a possible implementation using Json file serialization. (This codes requires the NewtonSoft Json nuget package.):

  1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:  #r @"..\packages\Newtonsoft.Json.7.0.1\lib\net45\Newtonsoft.Json.dll" let runResumable (p:Resumable<'h,'t>) fileName = let rec aux (state:'h, result) = System.IO.File.WriteAllText(fileName, Newtonsoft.Json.JsonConvert.SerializeObject(state)) match result with | NotExecuted -> aux (p state) | Result r -> (state, result) let initialState = if System.IO.File.Exists fileName then Newtonsoft.Json.JsonConvert.DeserializeObject<'h>(System.IO.File.ReadAllText(fileName)) else Zero.getZeroTyped<_> aux (initialState, NotExecuted) 

Now let's excute the service API and simulate a service interruption using an async timeout:

 1: 2: 3: 4:  let p = async { return runResumable MyResumableService.myServiceApi @"c:\temp\apiprogress.json" } let t = Async.RunSynchronously(p, 10) 

This first run prompts you to enter the machine name, it then submits the request to the cloud and starts polling for completion before failing due to the forced timeouts of 10ms. If you now run the same command once again:

 1:  let t2 = Async.RunSynchronously(p, 100) 

Not only you are not prompted a machine name but the machine name and request Id are restored from the previous run and the VM provisioning request completes successfully!

# Conclusion

I hope you enjoyed this article. For the sake of succinctness I am not going to repeat in the conclusion what I already discussed in this article so please refer to the abstract for that :-)

Possible improvements include support for other monadic operators (TryFinally, TryWith, Using) and integration with the async monad to enable definition of asynchronous resumable computations.

# References

There are plenty of references on monads on the internet. You can easily find them with your favourite search engine. One related topic in the monadic world is the so-called "state monad". Also I am sure somebody else already came up with something similar to the resuamble monad. I did not look in the literature myself, if you have a reference please send it to me through github and I'll add the reference here.

# Thanks

To Tomas Petricek for his marvelous literate programming package for F# that was used to typeset this HTML page.

val getMachineName : unit -> string

val printfn : format:Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val machineName : string
namespace System
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
val provisionVM : machineName:string -> int

val requestId : int
Multiple items
type Random =
new : unit -> Random + 1 overload
member Next : unit -> int + 2 overloads
member NextBytes : buffer:byte[] -> unit
member NextDouble : unit -> float

Full name: System.Random

--------------------
System.Random() : unit
System.Random(Seed: int) : unit
val vmRequestSucceeded : (int -> bool)

val waitTime : int ref
Multiple items
val ref : value:'T -> 'T ref

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

--------------------
type 'T ref = Ref<'T>

Full name: Microsoft.FSharp.Core.ref<_>
val aux : (int -> bool)
val myServiceApi : unit -> string * int

module Environment

val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
Multiple items
inherit CriticalFinalizerObject
member Abort : unit -> unit + 1 overload
member ApartmentState : ApartmentState with get, set
member CurrentCulture : CultureInfo with get, set
member CurrentUICulture : CultureInfo with get, set
member DisableComObjectEagerCleanup : unit -> unit
member ExecutionContext : ExecutionContext
member GetApartmentState : unit -> ApartmentState
member GetCompressedStack : unit -> CompressedStack
member GetHashCode : unit -> int
...

--------------------
type Cell<'t> =
| NotExecuted
| Result of 't

union case Cell.NotExecuted: Cell<'t>
union case Cell.Result: 't -> Cell<'t>
type Resumable<'h,'t> = 'h -> 'h * Cell<'t>

Multiple items
type ResumableBuilder =
new : unit -> ResumableBuilder
member Bind : f:Resumable<'u,'a> * g:('a -> Resumable<'v,'b>) -> Resumable<('u * Cell<'a> * 'v),'b>
member Combine : p1:Resumable<'u,unit> * p2:Resumable<'v,'b> -> Resumable<('u * Cell<unit> * 'v),'b>
member Delay : generator:(unit -> Resumable<'u,'a>) -> ('u -> 'u * Cell<'a>)
member Return : x:'t -> (unit -> unit * Cell<'t>)
member ReturnFrom : x:'a -> 'a
member While : gd:(unit -> bool) * prog:Resumable<unit,unit> -> Resumable<unit,unit>
member Zero : unit -> (unit -> unit * Cell<unit>)

--------------------
new : unit -> ResumableBuilder
member ResumableBuilder.Zero : unit -> (unit -> unit * Cell<unit>)

val __ : ResumableBuilder
member ResumableBuilder.Return : x:'t -> (unit -> unit * Cell<'t>)

val x : 't
member ResumableBuilder.ReturnFrom : x:'a -> 'a

val x : 'a
member ResumableBuilder.Delay : generator:(unit -> Resumable<'u,'a>) -> ('u -> 'u * Cell<'a>)

val generator : (unit -> Resumable<'u,'a>)
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val h : 'u
member ResumableBuilder.Bind : f:Resumable<'u,'a> * g:('a -> Resumable<'v,'b>) -> Resumable<('u * Cell<'a> * 'v),'b>

val f : Resumable<'u,'a>
val g : ('a -> Resumable<'v,'b>)
val u : 'u
val a : Cell<'a>
val v : 'v
val u_stepped : 'u
val a_stepped : Cell<'a>
val _a : 'a
val v_stepped : 'v
val b_stepped : Cell<'b>
member ResumableBuilder.Combine : p1:Resumable<'u,unit> * p2:Resumable<'v,'b> -> Resumable<('u * Cell<unit> * 'v),'b>

val p1 : Resumable<'u,unit>
val p2 : Resumable<'v,'b>
member ResumableBuilder.Bind : f:Resumable<'u,'a> * g:('a -> Resumable<'v,'b>) -> Resumable<('u * Cell<'a> * 'v),'b>
member ResumableBuilder.While : gd:(unit -> bool) * prog:Resumable<unit,unit> -> Resumable<unit,unit>

val gd : (unit -> bool)
val prog : Resumable<unit,unit>
val whileA : ((unit -> bool) -> (unit -> unit * Cell<unit>) -> unit -> unit * Cell<unit>)
val prog : (unit -> unit * Cell<unit>)
member ResumableBuilder.Zero : unit -> (unit -> unit * Cell<unit>)
val u : unit
val resumable : ResumableBuilder

val example : (unit * Cell<int> * (unit * Cell<int> * unit) -> (unit * Cell<int> * (unit * Cell<int> * unit)) * Cell<int * int>)

val x : int
val y : int
val s0 : unit * Cell<'a> * (unit * Cell<'b> * unit)

val s1 : unit * Cell<int> * (unit * Cell<int> * unit)

val r1 : Cell<int * int>

val s2 : unit * Cell<int> * (unit * Cell<int> * unit)

val r2 : Cell<int * int>

val s3 : unit * Cell<int> * (unit * Cell<int> * unit)

val r3 : Cell<int * 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<_>
val z0 : unit * Cell<'a> * (unit * Cell<'b> * unit)

val z : r:'v -> unit * Cell<'a> * 'v

val r : 'v
val z0_b : unit * Cell<int> * (unit * Cell<int> * unit)

val z0_c : unit * Cell<int> * (unit * Cell<int> * unit)

val z0_d : unit * Cell<int> * (unit * Cell<int> * unit)

namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Reflection
val getZeroUntyped<'X> : _type:System.Type -> obj

type Type =
inherit MemberInfo
member Assembly : Assembly
member AssemblyQualifiedName : string
member Attributes : TypeAttributes
member BaseType : Type
member ContainsGenericParameters : bool
member DeclaringMethod : MethodBase
member DeclaringType : Type
member Equals : o:obj -> bool + 1 overload
member FindInterfaces : filter:TypeFilter * filterCriteria:obj -> Type[]
member FindMembers : memberType:MemberTypes * bindingAttr:BindingFlags * filter:MemberFilter * filterCriteria:obj -> MemberInfo[]
...

Full name: System.Type
val _type : System.Type
val typeof<'T> : System.Type

Full name: Microsoft.FSharp.Core.Operators.typeof
val box : value:'T -> obj

Full name: Microsoft.FSharp.Core.Operators.box
property System.Type.IsGenericType: bool
System.Type.GetGenericTypeDefinition() : System.Type
val typedefof<'T> : System.Type

Full name: Microsoft.FSharp.Core.Operators.typedefof
type FSharpValue =
static member GetExceptionFields : exn:obj * ?bindingFlags:BindingFlags -> obj []
static member GetRecordField : record:obj * info:PropertyInfo -> obj
static member GetRecordFields : record:obj * ?bindingFlags:BindingFlags -> obj []
static member GetTupleField : tuple:obj * index:int -> obj
static member GetTupleFields : tuple:obj -> obj []
static member GetUnionFields : value:obj * unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo * obj []
static member MakeFunction : functionType:Type * implementation:(obj -> obj) -> obj
static member MakeRecord : recordType:Type * values:obj [] * ?bindingFlags:BindingFlags -> obj
static member MakeTuple : tupleElements:obj [] * tupleType:Type -> obj
static member MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?bindingFlags:BindingFlags -> obj
...

Full name: Microsoft.FSharp.Reflection.FSharpValue
static member FSharpValue.MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?allowAccessToPrivateRepresentation:bool -> obj
static member FSharpValue.MakeUnion : unionCase:UnionCaseInfo * args:obj [] * ?bindingFlags:System.Reflection.BindingFlags -> obj
type FSharpType =
static member GetExceptionFields : exceptionType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
static member GetFunctionElements : functionType:Type -> Type * Type
static member GetRecordFields : recordType:Type * ?bindingFlags:BindingFlags -> PropertyInfo []
static member GetTupleElements : tupleType:Type -> Type []
static member GetUnionCases : unionType:Type * ?bindingFlags:BindingFlags -> UnionCaseInfo []
static member IsExceptionRepresentation : exceptionType:Type * ?bindingFlags:BindingFlags -> bool
static member IsFunction : typ:Type -> bool
static member IsModule : typ:Type -> bool
static member IsRecord : typ:Type * ?bindingFlags:BindingFlags -> bool
static member IsTuple : typ:Type -> bool
...

Full name: Microsoft.FSharp.Reflection.FSharpType
static member FSharpType.GetUnionCases : unionType:System.Type * ?allowAccessToPrivateRepresentation:bool -> UnionCaseInfo []
static member FSharpType.GetUnionCases : unionType:System.Type * ?bindingFlags:System.Reflection.BindingFlags -> UnionCaseInfo []
static member FSharpValue.MakeTuple : tupleElements:obj [] * tupleType:System.Type -> obj
module Array

from Microsoft.FSharp.Collections
val map : mapping:('T -> 'U) -> array:'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map
val invalidArg : argumentName:string -> message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.invalidArg
val getZeroTyped<'X> : 'X

val unbox : value:obj -> 'T

Full name: Microsoft.FSharp.Core.Operators.unbox
module Tests

Multiple items
val string : value:'T -> string

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

--------------------
type string = System.String

Full name: Microsoft.FSharp.Core.string
val y : System.Type

type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
val myServiceApi : (unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit)) -> (unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))) * Cell<string * int * bool>)

val s1 : unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))

val r1 : Cell<string * int * bool>

module Zero

val s2 : unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))

val r2 : Cell<string * int * bool>

val s3 : unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))

val r3 : Cell<string * int * bool>

val s4 : unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))

val r4 : Cell<string * int * bool>

val s5 : unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))

val r5 : Cell<string * int * bool>

val runThroughCompletion : m:('a -> 'a * Cell<'b>) -> state:'a * result:Cell<'b> -> 'b

val m : ('a -> 'a * Cell<'b>)
val state : 'a
val result : Cell<'b>
val r : 'b
val run : m:('a -> 'a * Cell<'b>) -> 'b

module MyResumableService

val runResumable : p:Resumable<'h,'t> -> fileName:string -> 'h * Cell<'t>

val p : Resumable<'h,'t>
val fileName : string
val aux : ('h * Cell<'t> -> 'h * Cell<'t>)
val state : 'h
val result : Cell<'t>
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
val r : 't
val initialState : 'h
System.IO.File.Exists(path: string) : bool
System.IO.File.ReadAllText(path: string, encoding: System.Text.Encoding) : string
System.IO.File.Delete(path: string) : unit
val p : Async<(unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))) * Cell<string * int * bool>>

val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
val t : (unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))) * Cell<string * int * bool>

Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken

Full name: Microsoft.FSharp.Control.Async

--------------------
type Async<'T>

Full name: Microsoft.FSharp.Control.Async<_>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:System.Threading.CancellationToken -> 'T
val t2 : (unit * Cell<string> * (unit * Cell<int> * ((unit * Cell<unit> * unit) * Cell<bool> * unit))) * Cell<string * int * bool>

val x : int ref

val m2 : (unit * Cell<unit> * unit -> (unit * Cell<unit> * unit) * Cell<int * bool>)

val incr : cell:int ref -> unit

Full name: Microsoft.FSharp.Core.Operators.incr
val s1 : unit * Cell<unit> * unit

val r1 : Cell<int * bool>

val s2 : unit * Cell<unit> * unit

val r2 : Cell<int * bool>

val s3 : unit * Cell<unit> * unit

val r3 : Cell<int * bool>

val s4 : unit * Cell<unit> * unit

val r4 : Cell<int * bool>

val s5 : unit * Cell<unit> * unit

val r5 : Cell<int * bool>

module IfTest

val y : int ref

val iftest : (unit * Cell<unit> * unit -> (unit * Cell<unit> * unit) * Cell<int * bool>)

val s1 : unit * Cell<unit> * unit

val r1 : Cell<int * bool>

val s2 : unit * Cell<unit> * unit

val r2 : Cell<int * bool>

val s3 : unit * Cell<unit> * unit

val r3 : Cell<int * bool>