F# Implementation of Scala ZIO

This is a prototype implementation of Scala ZIO in F#. It aims to be a skeleton of ZIO features such that additional functions can be easily fleshed out.

Background

I recently went to a talk on Scala ZIO by John De Goes. ZIO is a type-safe, composable library for asynchronous and concurrent programming in Scala.

It takes a different approach to other Scala effects libraries in that it does not require the use of Higher-Kinded Types. Instead it uses a reader monad to provide access to IO effects (called ZIO Environment).

I came away wanting something similar in F#. A useful library that could be used in the outer IO layer to simplify and test IO dependency code. I started to play with some reader code but didn't think it would ultimately work out. In fact, it works really well.

IO

\[IO = Reader + Async + Result\]

The F# equivalent of ZIO type aliases are UIO<'r,'a> which represents effects without errors, and IO<'r,'a,'e> which represents effects with a possible error. IO combines reader, async and result into one unified computation expression.

1: 
type UIO<'r,'a> = UIO of ('r * Cancel -> ('a option -> unit) -> unit)
1: 
type IO<'r,'a,'e> = IO of ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit)

Reader

The reader part represents all the environment dependencies required in the computation expression. It is fully type-safe with types inferred including any library requirements such as Clock for the timeout. The computation expression can easily be tested by running with a test environment.

Async

At the IO layer thread pool threads need to be used in the most efficient way without any blocking. This usually means Async in F# or async/await in C# need to be used. They both join threads without a thread pool thread having to wait.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
    let race (UIO run1) (IO run2) : IO<'r,Choice<'a1,'a2>,'e1> =
        IO (fun env cont ->
            if Cancel.isSet env then cont None
            else
                let envChild = Cancel.add env
                let mutable o = 0
                ThreadPool.QueueUserWorkItem (fun _ ->
                    run1 envChild (fun a ->
                        if Interlocked.Exchange(&o,1) = 0 then
                            Cancel.set envChild
                            if Cancel.isSet env then cont None
                            else Option.map (Choice2Of2 >> Ok) a |> cont
                    )
                ) |> ignore
                ThreadPool.QueueUserWorkItem (fun _ ->
                    run2 envChild (fun a ->
                        if Interlocked.Exchange(&o,1) = 0 then
                            Cancel.set envChild
                            if Cancel.isSet env then cont None
                            else Option.map (Result.map Choice1Of2) a |> cont
                    )
                ) |> ignore
        )

With IO async is implemented directly using the thread pool. There are two main reasons for this. In IO exceptions are not part of control flow. Errors are first class and type-safe. Unrecoverable exceptions output the stack trace and exit the process. Cancellation is fully integrated into IO meaning in race, parallel and upon an error, computations are automatically cancelled, saving resources.

These with the final part dramatically simplify and optimise asynchronous IO code.

Result

The result part of IO represents possible errors in an integrated and type-safe way. The error type is inferred, and different error types are auto lifted into Choice<'a,'b> when combined. IO computations can be timed out and retried based on result using simple functions. Schedule is a powerful construct that can be combined several ways. I've replicated the structure from ZIO but not fully explored its uses.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
let programRetry noRetry =
    io {
        do! Logger.log "started"
        do! Console.writeLine "Please enter your name:"
        let! name = Console.readLine()
        do! Logger.log ("got name = " + name)
        let! thread =
            Persistence.persist name
            |> IO.timeout 1000
            |> IO.retry (Schedule.recurs noRetry)
            |> IO.fork
        do! Console.writeLine ("Hi "+name)
        do! thread
        do! Logger.log "finished"
        return 0
    }

Conclusion

When type inference worked for the dependencies I was surprised. When it was also possible to make it work for the errors I was amazed.

Computation expressions do not compose well. At the IO layer a solution is needed for dependencies in a testable way. The IO layer also needs to efficiently use the thread pool. Making errors type-safe and integrated in the IO logic completes this compelling trinity.

References

IO.fs
IOTests.fs
ZIO Overview
ZIO Data Types
The Death Of Final Tagless

Thanks

@jdegoes for ZIO and a great talk that made me want to do this.
@NinoFloris for useful async discussions.
@keithtpinson for the error auto lift idea.

union case Result.Ok: 'a -> Result<'a,'e>
union case Result.Error: 'e -> Result<'a,'e>
type Result<'a,'e> =
  | Ok of 'a
  | Error of 'e

Full name: 2019-03-29-io.Result<_,_>
val map : f:('a -> 'b) -> Result<'a,'e> -> Result<'b,'e>

Full name: 2019-03-29-io.Result.map
val f : ('a -> 'b)
val failwith : message:string -> 'T

Full name: Microsoft.FSharp.Core.Operators.failwith
val mapError : f:('e -> 'f) -> Result<'a,'e> -> Result<'a,'f>

Full name: 2019-03-29-io.Result.mapError
val f : ('e -> 'f)
Multiple items
union case Time.Time: Time

--------------------
type Time = | Time

Full name: 2019-03-29-io.Time
Multiple items
type Choice<'T1,'T2> =
  | Choice1Of2 of 'T1
  | Choice2Of2 of 'T2

Full name: Microsoft.FSharp.Core.Choice<_,_>

--------------------
type Choice<'T1,'T2,'T3> =
  | Choice1Of3 of 'T1
  | Choice2Of3 of 'T2
  | Choice3Of3 of 'T3

Full name: Microsoft.FSharp.Core.Choice<_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4> =
  | Choice1Of4 of 'T1
  | Choice2Of4 of 'T2
  | Choice3Of4 of 'T3
  | Choice4Of4 of 'T4

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5> =
  | Choice1Of5 of 'T1
  | Choice2Of5 of 'T2
  | Choice3Of5 of 'T3
  | Choice4Of5 of 'T4
  | Choice5Of5 of 'T5

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6> =
  | Choice1Of6 of 'T1
  | Choice2Of6 of 'T2
  | Choice3Of6 of 'T3
  | Choice4Of6 of 'T4
  | Choice5Of6 of 'T5
  | Choice6Of6 of 'T6

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
  | Choice1Of7 of 'T1
  | Choice2Of7 of 'T2
  | Choice3Of7 of 'T3
  | Choice4Of7 of 'T4
  | Choice5Of7 of 'T5
  | Choice6Of7 of 'T6
  | Choice7Of7 of 'T7

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_,_>
val merge : c:Choice<'a,'a> -> 'a

Full name: 2019-03-29-io.Choice.merge
val c : Choice<'a,'a>
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
val a : 'a
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
namespace System
namespace System.Threading
Multiple items
union case Cancel.Cancel: bool ref * children: Cancel list ref -> Cancel

--------------------
type Cancel = private | Cancel of bool ref * children: Cancel list ref

Full name: 2019-03-29-io.Cancel
type bool = System.Boolean

Full name: Microsoft.FSharp.Core.bool
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<_>
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
val set : elements:seq<'T> -> Set<'T> (requires comparison)

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.set
Multiple items
module List

from Microsoft.FSharp.Collections

--------------------
type List<'T> =
  | ( [] )
  | ( :: ) of Head: 'T * Tail: 'T list
  interface IEnumerable
  interface IEnumerable<'T>
  member GetSlice : startIndex:int option * endIndex:int option -> 'T list
  member Head : 'T
  member IsEmpty : bool
  member Item : index:int -> 'T with get
  member Length : int
  member Tail : 'T list
  static member Cons : head:'T * tail:'T list -> 'T list
  static member Empty : 'T list

Full name: Microsoft.FSharp.Collections.List<_>
val iter : action:('T -> unit) -> list:'T list -> unit

Full name: Microsoft.FSharp.Collections.List.iter
Multiple items
union case UIO.UIO: ('r * Cancel -> ('a option -> unit) -> unit) -> UIO<'r,'a>

--------------------
type UIO<'r,'a> =
  | UIO of ('r * Cancel -> ('a option -> unit) -> unit)
  member Bind : f:('a -> UIO<'r,'b>) -> UIO<'r,'b>
  member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.UIO<_,_>
type 'T option = Option<'T>

Full name: Microsoft.FSharp.Core.option<_>
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
val m : UIO<'r,'a>
member UIO.Bind : f:('a -> UIO<'r,'b>) -> UIO<'r,'b>

Full name: 2019-03-29-io.UIO`2.Bind
val f : ('a -> UIO<'r,'b>)
val run : ('r * Cancel -> ('a option -> unit) -> unit)
val env : 'r * Cancel
val cont : ('b option -> unit)
union case Option.None: Option<'T>
val o : 'a option
module Option

from Microsoft.FSharp.Core
val map : mapping:('T -> 'U) -> option:'T option -> 'U option

Full name: Microsoft.FSharp.Core.Option.map
union case Option.Some: Value: 'T -> Option<'T>
val run : ('r * Cancel -> ('b option -> unit) -> unit)
val result : a:'a -> UIO<'r,'a>

Full name: 2019-03-29-io.UIO.result
val cont : ('a option -> unit)
val effect : f:('r -> 'a) -> UIO<'r,'a>

Full name: 2019-03-29-io.UIO.effect
val f : ('r -> 'a)
val fst : tuple:('T1 * 'T2) -> 'T1

Full name: Microsoft.FSharp.Core.Operators.fst
val map : f:('a -> 'b) -> UIO<'r,'a> -> UIO<'r,'b>

Full name: 2019-03-29-io.UIO.map
val b : 'b
val delay : milliseconds:int -> UIO<'r,unit>

Full name: 2019-03-29-io.UIO.delay
val milliseconds : int
val cont : (unit option -> unit)
val mutable t : Timer
module Unchecked

from Microsoft.FSharp.Core.Operators
val defaultof<'T> : 'T

Full name: Microsoft.FSharp.Core.Operators.Unchecked.defaultof
Multiple items
type Timer =
  inherit MarshalByRefObject
  new : callback:TimerCallback -> Timer + 4 overloads
  member Change : dueTime:int * period:int -> bool + 3 overloads
  member Dispose : unit -> unit + 1 overload

Full name: System.Threading.Timer

--------------------
Timer(callback: TimerCallback) : unit
Timer(callback: TimerCallback, state: obj, dueTime: int, period: int) : unit
Timer(callback: TimerCallback, state: obj, dueTime: System.TimeSpan, period: System.TimeSpan) : unit
Timer(callback: TimerCallback, state: obj, dueTime: uint32, period: uint32) : unit
Timer(callback: TimerCallback, state: obj, dueTime: int64, period: int64) : unit
Timer.Dispose() : unit
Timer.Dispose(notifyObject: WaitHandle) : bool
type Timeout =
  static val Infinite : int

Full name: System.Threading.Timeout
field Timeout.Infinite = -1
val flatten : f:('r -> UIO<'r,'a>) -> UIO<'r,'a>

Full name: 2019-03-29-io.UIO.flatten
val f : ('r -> UIO<'r,'a>)
val t : 'a option
val toAsync : env:'r -> UIO<'r,'a> -> Async<'a>

Full name: 2019-03-29-io.UIO.toAsync
val env : 'r
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 AwaitTask : task:Task -> Async<unit>
static member AwaitTask : task:Task<'T> -> Async<'T>
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 StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'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.FromContinuations : callback:(('T -> unit) * (exn -> unit) * (System.OperationCanceledException -> unit) -> unit) -> Async<'T>
val cont : ('a -> unit)
property Option.Value: 'a
val fork : UIO<'r,'a> -> UIO<'r,UIO<'r,'a>>

Full name: 2019-03-29-io.UIO.fork
val contFork : (UIO<'r,'a> option -> unit)
val mutable o : obj
type ThreadPool =
  static member BindHandle : osHandle:nativeint -> bool + 1 overload
  static member GetAvailableThreads : workerThreads:int * completionPortThreads:int -> unit
  static member GetMaxThreads : workerThreads:int * completionPortThreads:int -> unit
  static member GetMinThreads : workerThreads:int * completionPortThreads:int -> unit
  static member QueueUserWorkItem : callBack:WaitCallback -> bool + 1 overload
  static member RegisterWaitForSingleObject : waitObject:WaitHandle * callBack:WaitOrTimerCallback * state:obj * millisecondsTimeOutInterval:uint32 * executeOnlyOnce:bool -> RegisteredWaitHandle + 3 overloads
  static member SetMaxThreads : workerThreads:int * completionPortThreads:int -> bool
  static member SetMinThreads : workerThreads:int * completionPortThreads:int -> bool
  static member UnsafeQueueNativeOverlapped : overlapped:NativeOverlapped -> bool
  static member UnsafeQueueUserWorkItem : callBack:WaitCallback * state:obj -> bool
  ...

Full name: System.Threading.ThreadPool
ThreadPool.QueueUserWorkItem(callBack: WaitCallback) : bool
ThreadPool.QueueUserWorkItem(callBack: WaitCallback, state: obj) : bool
val a : 'a option
val o : obj
type Interlocked =
  static member Add : location1:int * value:int -> int + 1 overload
  static member CompareExchange : location1:int * value:int * comparand:int -> int + 6 overloads
  static member Decrement : location:int -> int + 1 overload
  static member Exchange : location1:int * value:int -> int + 6 overloads
  static member Increment : location:int -> int + 1 overload
  static member Read : location:int64 -> int64

Full name: System.Threading.Interlocked
Interlocked.CompareExchange<'T (requires reference type)>(location1: byref<'T>, value: 'T, comparand: 'T) : 'T
Interlocked.CompareExchange(location1: byref<nativeint>, value: nativeint, comparand: nativeint) : nativeint
Interlocked.CompareExchange(location1: byref<obj>, value: obj, comparand: obj) : obj
Interlocked.CompareExchange(location1: byref<float>, value: float, comparand: float) : float
Interlocked.CompareExchange(location1: byref<float32>, value: float32, comparand: float32) : float32
Interlocked.CompareExchange(location1: byref<int64>, value: int64, comparand: int64) : int64
Interlocked.CompareExchange(location1: byref<int>, value: int, comparand: int) : int
val isNull : value:'T -> bool (requires 'T : null)

Full name: Microsoft.FSharp.Core.Operators.isNull
val not : value:bool -> bool

Full name: Microsoft.FSharp.Core.Operators.not
val cont : (Option<'a> -> unit)
val ignore : value:'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
type ClockService =
  interface
    abstract member Sleep : int -> UIO<'r,unit>
    abstract member Time : unit -> UIO<'r,Time>
  end

Full name: 2019-03-29-io.ClockService
Multiple items
abstract member ClockService.Time : unit -> UIO<'r,Time>

Full name: 2019-03-29-io.ClockService.Time

--------------------
type Time = | Time

Full name: 2019-03-29-io.Time
Multiple items
union case UIO.UIO: ('r * Cancel -> ('a option -> unit) -> unit) -> UIO<'r,'a>

--------------------
module UIO

from 2019-03-29-io

--------------------
type UIO<'r,'a> =
  | UIO of ('r * Cancel -> ('a option -> unit) -> unit)
  member Bind : f:('a -> UIO<'r,'b>) -> UIO<'r,'b>
  member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.UIO<_,_>
abstract member ClockService.Sleep : int -> UIO<'r,unit>

Full name: 2019-03-29-io.ClockService.Sleep
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 Clock =
  interface
    abstract member Clock : ClockService
  end

Full name: 2019-03-29-io.Clock
Multiple items
abstract member Clock.Clock : ClockService

Full name: 2019-03-29-io.Clock.Clock

--------------------
type Clock =
  interface
    abstract member Clock : ClockService
  end

Full name: 2019-03-29-io.Clock
Multiple items
union case Decision.Decision: cont: bool * delay: int * state: 'a * (unit -> 'b) -> Decision<'a,'b>

--------------------
type Decision<'a,'b> = | Decision of cont: bool * delay: int * state: 'a * (unit -> 'b)

Full name: 2019-03-29-io.Decision<_,_>
Multiple items
union case Schedule.Schedule: initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>> -> Schedule<'r,'s,'a,'b>

--------------------
type Schedule<'r,'s,'a,'b> = private | Schedule of initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>>

Full name: 2019-03-29-io.Schedule<_,_,_,_>
val forever<'r,'a> : Schedule<'r,int,'a,int>

Full name: 2019-03-29-io.Schedule.forever
val s : int
val private updated : update:(('a * 's -> UIO<'r,Decision<'s,'b>>) -> 'a * 's -> UIO<'r,Decision<'s,'b2>>) -> Schedule<'r,'s,'a,'b> -> Schedule<'r,'s,'a,'b2>

Full name: 2019-03-29-io.Schedule.updated
val update : (('a * 's -> UIO<'r,Decision<'s,'b>>) -> 'a * 's -> UIO<'r,Decision<'s,'b2>>)
val i : UIO<'r,'s>
val u : ('a * 's -> UIO<'r,Decision<'s,'b>>)
val private check : test:('a * 'b -> UIO<'r,bool>) -> m:Schedule<'r,'a0,'a,'b> -> Schedule<'r,'a0,'a,'b>

Full name: 2019-03-29-io.Schedule.check
val test : ('a * 'b -> UIO<'r,bool>)
val m : Schedule<'r,'a,'a0,'b>
val update : ('a * 'a0 -> UIO<'r,Decision<'a0,'b>>)
val s : 'a
val cont : bool
val dur : int
val a1 : 'a
val fb : (unit -> 'b)
val d : Decision<'a,'b>
val b : bool
val whileOutput : f:('b -> bool) -> m:Schedule<'a,'b0,'c,'b> -> Schedule<'a,'b0,'c,'b>

Full name: 2019-03-29-io.Schedule.whileOutput
val f : ('b -> bool)
val m : Schedule<'a,'b,'c,'b0>
val recurs : n:int -> Schedule<'a,int,'b,int>

Full name: 2019-03-29-io.Schedule.recurs
val n : int
val i : int
Multiple items
union case IO.IO: ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit) -> IO<'r,'a,'e>

--------------------
type IO<'r,'a,'e> =
  | IO of ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit)
  member Bind : f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e>
  member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>
  member Bind : f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e,'e2>>

Full name: 2019-03-29-io.IO<_,_,_>
Multiple items
module Result

from 2019-03-29-io

--------------------
type Result<'a,'e> =
  | Ok of 'a
  | Error of 'e

Full name: 2019-03-29-io.Result<_,_>
val m : IO<'r,'a,'e>
member IO.Bind : f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.IO`3.Bind
val run : ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit)
val cont : (Result<'b,'e> option -> unit)
val o : Result<'a,'e> option
val o : 'b option
val e : 'e
member IO.Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.IO`3.Bind
val f : ('a -> IO<'r,'b,'e>)
val run : ('r * Cancel -> (Result<'b,'e> option -> unit) -> unit)
member IO.Bind : f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e,'e2>>

Full name: 2019-03-29-io.IO`3.Bind
val f : ('a -> IO<'r,'b,'e2>)
Multiple items
module Choice

from 2019-03-29-io

--------------------
type Choice<'T1,'T2> =
  | Choice1Of2 of 'T1
  | Choice2Of2 of 'T2

Full name: Microsoft.FSharp.Core.Choice<_,_>

--------------------
type Choice<'T1,'T2,'T3> =
  | Choice1Of3 of 'T1
  | Choice2Of3 of 'T2
  | Choice3Of3 of 'T3

Full name: Microsoft.FSharp.Core.Choice<_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4> =
  | Choice1Of4 of 'T1
  | Choice2Of4 of 'T2
  | Choice3Of4 of 'T3
  | Choice4Of4 of 'T4

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5> =
  | Choice1Of5 of 'T1
  | Choice2Of5 of 'T2
  | Choice3Of5 of 'T3
  | Choice4Of5 of 'T4
  | Choice5Of5 of 'T5

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6> =
  | Choice1Of6 of 'T1
  | Choice2Of6 of 'T2
  | Choice3Of6 of 'T3
  | Choice4Of6 of 'T4
  | Choice5Of6 of 'T5
  | Choice6Of6 of 'T6

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_>

--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
  | Choice1Of7 of 'T1
  | Choice2Of7 of 'T2
  | Choice3Of7 of 'T3
  | Choice4Of7 of 'T4
  | Choice5Of7 of 'T5
  | Choice6Of7 of 'T6
  | Choice7Of7 of 'T7

Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_,_>
val cont : (Result<'b,Choice<'e,'e2>> option -> unit)
val bind : ('r * Cancel -> (Result<'b,'e2> option -> unit) -> unit)
val o : Result<'b,'e2> option
val b : Result<'b,Choice<'e,'e2>> option
member UIO.Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.UIO`2.Bind
val bind : ('r * Cancel -> (Result<'b,'e> option -> unit) -> unit)
val ok : a:'a -> IO<'r,'a,'e>

Full name: 2019-03-29-io.IO.ok
val cont : (Result<'a,'e> option -> unit)
val error : e:'e -> IO<'r,'a,'e>

Full name: 2019-03-29-io.IO.error
val result : a:Result<'a,'e> -> IO<'r,'a,'e>

Full name: 2019-03-29-io.IO.result
val a : Result<'a,'e>
val effect : f:('r -> Result<'a,'e>) -> IO<'r,'a,'e>

Full name: 2019-03-29-io.IO.effect
val f : ('r -> Result<'a,'e>)
val map : f:('a -> 'b) -> IO<'r,'a,'e> -> IO<'r,'b,'e>

Full name: 2019-03-29-io.IO.map
val b : Result<'b,'e> option
val mapError : f:('e -> 'e2) -> IO<'r,'a,'e> -> IO<'r,'a,'e2>

Full name: 2019-03-29-io.IO.mapError
val f : ('e -> 'e2)
val cont : (Result<'a,'e2> option -> unit)
val b : Result<'a,'e2> option
val mapResult : f:(Result<'a,'e> -> Result<'b,'e2>) -> IO<'r,'a,'e> -> IO<'r,'b,'e2>

Full name: 2019-03-29-io.IO.mapResult
val f : (Result<'a,'e> -> Result<'b,'e2>)
val cont : (Result<'b,'e2> option -> unit)
val b : Result<'b,'e2> option
val private foldM : succ:('a -> IO<'r,'b,'e2>) -> err:('e -> IO<'r,'b,'e2>) -> IO<'r,'a,'e> -> IO<'r,'b,'e2>

Full name: 2019-03-29-io.IO.foldM
val succ : ('a -> IO<'r,'b,'e2>)
val err : ('e -> IO<'r,'b,'e2>)
val run : ('r * Cancel -> (Result<'b,'e2> option -> unit) -> unit)
val private retryOrElse : Schedule<'r,'s,'e,'a> -> orElse:('e * 's -> IO<'r,'b,'e2>) -> io:IO<'r,'a0,'e> -> IO<'r,Choice<'a0,'b>,'e2>

Full name: 2019-03-29-io.IO.retryOrElse
Multiple items
union case Schedule.Schedule: initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>> -> Schedule<'r,'s,'a,'b>

--------------------
module Schedule

from 2019-03-29-io

--------------------
type Schedule<'r,'s,'a,'b> = private | Schedule of initial: UIO<'r,'s> * update: 'a * 's -> UIO<'r,Decision<'s,'b>>

Full name: 2019-03-29-io.Schedule<_,_,_,_>
val initial : UIO<'r,'s>
val update : ('e * 's -> UIO<'r,Decision<'s,'a>>)
val orElse : ('e * 's -> IO<'r,'b,'e2>)
val io : IO<'r,'a,'e>
val loop : ('s -> IO<'r,Choice<'a,'b>,'e2>)
val state : 's
val u : UIO<'r,Decision<'s,'a>>
member UIO.Bind : f:('a -> UIO<'r,'b>) -> UIO<'r,'b>
member UIO.Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>
val delay : int
val retry : policy:Schedule<'r,'s,'e,'sb> -> io:IO<'r,'a,'e> -> IO<'r,'a,'e>

Full name: 2019-03-29-io.IO.retry
val policy : Schedule<'r,'s,'e,'sb>
val fork : IO<'r,'a,'e> -> UIO<'r,IO<'r,'a,'e>>

Full name: 2019-03-29-io.IO.fork
val contFork : (IO<'r,'a,'e> option -> unit)
val a : Result<'a,'e> option
val para : ios:IO<'r,'a,'e> [] -> IO<'r,'a [],'e>

Full name: 2019-03-29-io.IO.para
val ios : IO<'r,'a,'e> []
val cont : (Result<'a [],'e> option -> unit)
val envChild : 'r * Cancel
val results : 'a []
module Array

from Microsoft.FSharp.Collections
val zeroCreate : count:int -> 'T []

Full name: Microsoft.FSharp.Collections.Array.zeroCreate
property System.Array.Length: int
val mutable count : int
val iteri : action:(int -> 'T -> unit) -> array:'T [] -> unit

Full name: Microsoft.FSharp.Collections.Array.iteri
Interlocked.Decrement(location: byref<int64>) : int64
Interlocked.Decrement(location: byref<int>) : int
Interlocked.Exchange<'T (requires reference type)>(location1: byref<'T>, value: 'T) : 'T
Interlocked.Exchange(location1: byref<nativeint>, value: nativeint) : nativeint
Interlocked.Exchange(location1: byref<obj>, value: obj) : obj
Interlocked.Exchange(location1: byref<float>, value: float) : float
Interlocked.Exchange(location1: byref<float32>, value: float32) : float32
Interlocked.Exchange(location1: byref<int64>, value: int64) : int64
Interlocked.Exchange(location1: byref<int>, value: int) : int
val race : UIO<'a,'a2> -> IO<'a,'a1,'e1> -> IO<'r,Choice<'a1,'a2>,'e1>

Full name: 2019-03-29-io.IO.race
val run1 : ('a * Cancel -> ('a2 option -> unit) -> unit)
val run2 : ('a * Cancel -> (Result<'a1,'e1> option -> unit) -> unit)
val cont : (Result<Choice<'a1,'a2>,'e1> option -> unit)
val envChild : 'a * Cancel
val mutable o : int
val a : 'a2 option
val a : Result<'a1,'e1> option
val timeout : milliseconds:int -> io:IO<'r,'a,'e> -> IO<'r,'a,'e option>

Full name: 2019-03-29-io.IO.timeout
val cont : (Result<'a,'e option> option -> unit)
val run : ('r * Cancel -> (Result<Choice<'a,unit>,'e> option -> unit) -> unit)
val o : Result<Choice<'a,unit>,'e> option
val toAsync : env:'r -> IO<'r,'a,'e> -> Async<Result<'a,'e>>

Full name: 2019-03-29-io.IO.toAsync
val cont : (Result<'a,'e> -> unit)
property Option.Value: Result<'a,'e>
Multiple items
type IOBuilder =
  new : unit -> IOBuilder
  member Bind : io:UIO<'r,'a> * f:('a -> UIO<'r,'b>) -> UIO<'r,'b>
  member Bind : io:IO<'r,'a,'e> * f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e>
  member Bind : io:UIO<'r,'a> * f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>
  member Bind : io:IO<'r,'a,'e> * f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>
  member Bind : io:IO<'r,'a,'e1> * f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e1,'e2>>
  member Return : value:'b -> UIO<'c,'b>
  member ReturnFrom : value:'a -> 'a

Full name: 2019-03-29-io.IOBuilder

--------------------
new : unit -> IOBuilder
member IOBuilder.Bind : io:UIO<'r,'a> * f:('a -> UIO<'r,'b>) -> UIO<'r,'b>

Full name: 2019-03-29-io.IOBuilder.Bind
val io : UIO<'r,'a>
val __ : IOBuilder
member IOBuilder.Bind : io:IO<'r,'a,'e> * f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.IOBuilder.Bind
Multiple items
union case IO.IO: ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit) -> IO<'r,'a,'e>

--------------------
module IO

from 2019-03-29-io

--------------------
type IO<'r,'a,'e> =
  | IO of ('r * Cancel -> (Result<'a,'e> option -> unit) -> unit)
  member Bind : f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e>
  member Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>
  member Bind : f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e,'e2>>

Full name: 2019-03-29-io.IO<_,_,_>
member IO.Bind : f:('a -> UIO<'r,'b>) -> IO<'r,'b,'e>
member IO.Bind : f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>
member IO.Bind : f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e,'e2>>
member IOBuilder.Bind : io:UIO<'r,'a> * f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.IOBuilder.Bind
member IOBuilder.Bind : io:IO<'r,'a,'e> * f:('a -> IO<'r,'b,'e>) -> IO<'r,'b,'e>

Full name: 2019-03-29-io.IOBuilder.Bind
member IOBuilder.Bind : io:IO<'r,'a,'e1> * f:('a -> IO<'r,'b,'e2>) -> IO<'r,'b,Choice<'e1,'e2>>

Full name: 2019-03-29-io.IOBuilder.Bind
val io : IO<'r,'a,'e1>
member IOBuilder.Return : value:'b -> UIO<'c,'b>

Full name: 2019-03-29-io.IOBuilder.Return
val value : 'b
member IOBuilder.ReturnFrom : value:'a -> 'a

Full name: 2019-03-29-io.IOBuilder.ReturnFrom
val value : 'a
Multiple items
type AutoOpenAttribute =
  inherit Attribute
  new : unit -> AutoOpenAttribute
  new : path:string -> AutoOpenAttribute
  member Path : string

Full name: Microsoft.FSharp.Core.AutoOpenAttribute

--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
val io : IOBuilder

Full name: 2019-03-29-io.IOAutoOpen.io
Multiple items
union case ConsoleError.ConsoleError: ConsoleError

--------------------
type ConsoleError = | ConsoleError

Full name: 2019-03-29-io.ConsoleError
type ConsoleService =
  interface
    abstract member ReadLine : unit -> Result<string,ConsoleError>
    abstract member WriteLine : string -> unit
  end

Full name: 2019-03-29-io.ConsoleService
abstract member ConsoleService.WriteLine : string -> unit

Full name: 2019-03-29-io.ConsoleService.WriteLine
Multiple items
val string : value:'T -> string

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

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

Full name: Microsoft.FSharp.Core.string
abstract member ConsoleService.ReadLine : unit -> Result<string,ConsoleError>

Full name: 2019-03-29-io.ConsoleService.ReadLine
type Console =
  interface
    abstract member Console : ConsoleService
  end

Full name: 2019-03-29-io.Console
Multiple items
abstract member Console.Console : ConsoleService

Full name: 2019-03-29-io.Console.Console

--------------------
type Console =
  interface
    abstract member Console : ConsoleService
  end

Full name: 2019-03-29-io.Console
type LoggingService =
  interface
    abstract member Log : string -> unit
  end

Full name: 2019-03-29-io.LoggingService
abstract member LoggingService.Log : string -> unit

Full name: 2019-03-29-io.LoggingService.Log
type Logger =
  interface
    abstract member Logging : LoggingService
  end

Full name: 2019-03-29-io.Logger
abstract member Logger.Logging : LoggingService

Full name: 2019-03-29-io.Logger.Logging
val log : value:'T -> 'T (requires member Log)

Full name: Microsoft.FSharp.Core.Operators.log
Multiple items
union case PersistError.PersistError: PersistError

--------------------
type PersistError = | PersistError

Full name: 2019-03-29-io.PersistError
type PersistenceService =
  interface
    abstract member Persist : 'a -> Result<unit,PersistError>
  end

Full name: 2019-03-29-io.PersistenceService
abstract member PersistenceService.Persist : 'a -> Result<unit,PersistError>

Full name: 2019-03-29-io.PersistenceService.Persist
type Persistence =
  interface
    abstract member Persistence : PersistenceService
  end

Full name: 2019-03-29-io.Persistence
Multiple items
abstract member Persistence.Persistence : PersistenceService

Full name: 2019-03-29-io.Persistence.Persistence

--------------------
type Persistence =
  interface
    abstract member Persistence : PersistenceService
  end

Full name: 2019-03-29-io.Persistence
val programRetry : noRetry:int -> IO<'r,int,Choice<ConsoleError,PersistError option>>

Full name: 2019-03-29-io.programRetry
val noRetry : int
val name : 'a
val thread : IO<'a,unit,'b option>