Orsak


Basic usage

This is a library intended to make effectful code easier to manage. It allows the user to easily create and consume side-effects modelled as interfaces, by making use of computation expressions.

Imagine having the interface

type IButtonPusher =
    abstract member PushButton: unit -> Task<unit>

describing some business-critical capability. To integrate that with the effect framework, we create the following helpers:

type IButtonPusherProvider =
    abstract member ButtonPusher: IButtonPusher

open Orsak

module ButtonPusher =
    let pushButton () =
        Effect.Create(fun (provider: #IButtonPusherProvider) -> provider.ButtonPusher.PushButton())

Then, we can the write code like

let work () : Effect<#IButtonPusherProvider, unit, _> = eff {
    //code
    do! ButtonPusher.pushButton ()
//more code
}

Which can be run with some type which implements IButtonPusherProvider.

The big win with effects is that they compose. By using a flexible constraint in the effect creation function, we can simply add effects, which will constrain the effect runner type to implement both IButtonPusherProvider and ILoggerFactory

type ILoggerProvider =
    abstract member Logger: ILogger

let logInformation a =
    Effect.Create(fun (provider: #ILoggerProvider) -> provider.Logger.Log(LogLevel.Information, a))

let workThenLog () = eff {
    do! work ()
    do! logInformation "Did the work."
}

Then, in the composition root of the program, one could define:

type EffectRunner(buttonPusher: IButtonPusher) =
    interface ILoggerProvider with
        member _.Logger = NullLogger.Instance

    interface IButtonPusherProvider with
        member _.ButtonPusher = buttonPusher

And then run the effects:

let runner: EffectRunner =
    EffectRunner(
        { new IButtonPusher with
            member this.PushButton() = Task.FromResult()
        }
    )

workThenLog().Run(runner)
namespace System
namespace System.Threading
namespace System.Threading.Tasks
namespace Microsoft
namespace Microsoft.Extensions
namespace Microsoft.Extensions.Logging
namespace Microsoft.Extensions.Logging.Abstractions
type IButtonPusher = abstract PushButton: unit -> Task<unit>
type unit = Unit
Multiple items
type Task = interface IAsyncResult interface IDisposable new: action: Action -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable member ContinueWith: continuationAction: Action<Task,obj> * state: obj -> Task + 19 overloads member Dispose: unit -> unit member GetAwaiter: unit -> TaskAwaiter member RunSynchronously: unit -> unit + 1 overload member Start: unit -> unit + 1 overload member Wait: unit -> unit + 5 overloads ...
<summary>Represents an asynchronous operation.</summary>

--------------------
type Task<'TResult> = inherit Task new: ``function`` : Func<obj,'TResult> * state: obj -> unit + 7 overloads member ConfigureAwait: continueOnCapturedContext: bool -> ConfiguredTaskAwaitable<'TResult> member ContinueWith: continuationAction: Action<Task<'TResult>,obj> * state: obj -> Task + 19 overloads member GetAwaiter: unit -> TaskAwaiter<'TResult> member WaitAsync: cancellationToken: CancellationToken -> Task<'TResult> + 2 overloads member Result: 'TResult static member Factory: TaskFactory<'TResult>
<summary>Represents an asynchronous operation that can return a value.</summary>
<typeparam name="TResult">The type of the result produced by this <see cref="T:System.Threading.Tasks.Task`1" />.</typeparam>


--------------------
Task(action: System.Action) : Task
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken) : Task
Task(action: System.Action, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj) : Task
Task(action: System.Action, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken) : Task
Task(action: System.Action<obj>, state: obj, creationOptions: TaskCreationOptions) : Task
Task(action: System.Action<obj>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task

--------------------
Task(``function`` : System.Func<'TResult>) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<'TResult>, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
Task(``function`` : System.Func<obj,'TResult>, state: obj, cancellationToken: System.Threading.CancellationToken, creationOptions: TaskCreationOptions) : Task<'TResult>
type IButtonPusherProvider = abstract ButtonPusher: IButtonPusher
namespace Orsak
val pushButton: unit -> Effect<#IButtonPusherProvider,unit,'b>
Multiple items
union case Effect.Effect: EffectDelegate<'r,'a,'e> -> Effect<'r,'a,'e>

--------------------
module Effect from Orsak

--------------------
type Effect = static member Create: f: ('a -> Task<Result<'b,'e>>) -> Effect<'a,'b,'e> + 2 overloads static member Error: error: 'e -> Effect<'a,'b,'e>

--------------------
[<Struct>] type Effect<'r,'a,'e> = | Effect of EffectDelegate<'r,'a,'e> member Run: r: 'r -> AsyncResult<'a,'e> member RunOrFail: r: 'r -> Task<'a> static member (<*>) : f: Effect<'a3,('a4 -> 'a5),'a6> * e: Effect<'a3,'a4,'a6> -> Effect<'a3,'a5,'a6> static member (>>=) : h: Effect<'r,'a,'e> * f: ('a -> Effect<'r,'b,'e>) -> Effect<'r,'b,'e> static member Map: h: Effect<'r,'a,'e> * f: ('a -> 'b) -> Effect<'r,'b,'e> static member Return: a: 'a3 -> Effect<'a4,'a3,'a5> static member ap: applicative: Effect<'r,('b -> 'a),'e> -> e: Effect<'r,'b,'e> -> Effect<'r,'a,'e>
<summary> Describes an effect that can either succeed with <typeparamref name="'r" />, or fails with <typeparamref name="'e" />. The effect is is 'cold', and only starts when run with an <typeparamref name="'r" />. </summary>
<typeparam name="'r"> The environment required to run the effect </typeparam>
<typeparam name="'a"> The resulting type when the effect runs successfully </typeparam>
<typeparam name="'e"> The resulting type when the effect fails</typeparam>
<returns></returns>
static member Effect.Create: f: ('a -> 'b) -> Effect<'a,'b,'e>
static member Effect.Create: f: ('a -> Async<'b>) -> Effect<'a,'b,'a2>
static member Effect.Create: f: ('a -> Result<'b,'e>) -> Effect<'a,'b,'e>
static member Effect.Create: f: ('a -> Task<'b>) -> Effect<'a,'b,'c>
static member Effect.Create: f: ('a -> ValueTask<'b>) -> Effect<'a,'b,'a2>
static member Effect.Create: f: ('a -> Async<Result<'b,'e>>) -> Effect<'a,'b,'e>
static member Effect.Create: f: ('a -> ValueTask<Result<'b,'e>>) -> Effect<'a,'b,'e>
static member Effect.Create: f: ('a -> Task<Result<'b,'e>>) -> Effect<'a,'b,'e>
val provider: #IButtonPusherProvider
property IButtonPusherProvider.ButtonPusher: IButtonPusher with get
abstract IButtonPusher.PushButton: unit -> Task<unit>
val work: unit -> Effect<#IButtonPusherProvider,unit,'b>
val eff: EffBuilder
module ButtonPusher from 1_Motivation
type ILoggerProvider = abstract Logger: ILogger
Multiple items
type Logger<'T> = interface ILogger<'T> interface ILogger new: factory: ILoggerFactory -> unit
<summary> Delegates to a new <see cref="T:Microsoft.Extensions.Logging.ILogger" /> instance using the full name of the given type, created by the provided <see cref="T:Microsoft.Extensions.Logging.ILoggerFactory" />. </summary>
<typeparam name="T">The type.</typeparam>


--------------------
Logger(factory: ILoggerFactory) : Logger<'T>
Multiple items
type ILogger = member BeginScope<'TState> : state: 'TState -> IDisposable member IsEnabled: logLevel: LogLevel -> bool member Log<'TState> : logLevel: LogLevel * eventId: EventId * state: 'TState * ``exception`` : exn * formatter: Func<'TState,exn,string> -> unit
<summary> Represents a type used to perform logging. </summary>
<remarks>Aggregates most logging patterns to a single method.</remarks>


--------------------
type ILogger<'TCategoryName> = inherit ILogger
<summary> A generic interface for logging where the category name is derived from the specified <typeparamref name="TCategoryName" /> type name. Generally used to enable activation of a named <see cref="T:Microsoft.Extensions.Logging.ILogger" /> from dependency injection. </summary>
<typeparam name="TCategoryName">The type whose name is used for the logger category name.</typeparam>
val logInformation: a: string -> Effect<#ILoggerProvider,unit,'b>
val a: string
val provider: #ILoggerProvider
property ILoggerProvider.Logger: ILogger with get
(extension) ILogger.Log(logLevel: LogLevel, message: string, [<System.ParamArray>] args: obj array) : unit
(extension) ILogger.Log(logLevel: LogLevel, eventId: EventId, message: string, [<System.ParamArray>] args: obj array) : unit
(extension) ILogger.Log(logLevel: LogLevel, ``exception`` : exn, message: string, [<System.ParamArray>] args: obj array) : unit
(extension) ILogger.Log(logLevel: LogLevel, eventId: EventId, ``exception`` : exn, message: string, [<System.ParamArray>] args: obj array) : unit
ILogger.Log<'TState>(logLevel: LogLevel, eventId: EventId, state: 'TState, ``exception`` : exn, formatter: System.Func<'TState,exn,string>) : unit
[<Struct>] type LogLevel = | Trace = 0 | Debug = 1 | Information = 2 | Warning = 3 | Error = 4 | Critical = 5 | None = 6
<summary> Defines logging severity levels. </summary>
field LogLevel.Information: LogLevel = 2
val workThenLog: unit -> Effect<'a,unit,'b> (requires 'a :> ILoggerProvider and 'a :> IButtonPusherProvider)
Multiple items
type EffectRunner = interface IButtonPusherProvider interface ILoggerProvider new: buttonPusher: IButtonPusher -> EffectRunner

--------------------
new: buttonPusher: IButtonPusher -> EffectRunner
val buttonPusher: IButtonPusher
Multiple items
type NullLogger = interface ILogger member BeginScope<'TState> : state: 'TState -> IDisposable member IsEnabled: logLevel: LogLevel -> bool member Log<'TState> : logLevel: LogLevel * eventId: EventId * state: 'TState * ``exception`` : exn * formatter: Func<'TState,exn,string> -> unit static member Instance: NullLogger
<summary> Minimalistic logger that does nothing. </summary>

--------------------
type NullLogger<'T> = interface ILogger<'T> interface ILogger new: unit -> unit member BeginScope<'TState> : state: 'TState -> IDisposable member IsEnabled: logLevel: LogLevel -> bool member Log<'TState> : logLevel: LogLevel * eventId: EventId * state: 'TState * ``exception`` : exn * formatter: Func<'TState,exn,string> -> unit static val Instance: NullLogger<'T>
<summary> Minimalistic logger that does nothing. </summary>

--------------------
NullLogger() : NullLogger<'T>
Multiple items
field NullLogger<'T>.Instance: NullLogger<'T>
<summary> Returns an instance of <see cref="T:Microsoft.Extensions.Logging.Abstractions.NullLogger`1" />. </summary>
<returns>An instance of <see cref="T:Microsoft.Extensions.Logging.Abstractions.NullLogger`1" />.</returns>


--------------------
property NullLogger.Instance: NullLogger with get
<summary> Returns the shared instance of <see cref="T:Microsoft.Extensions.Logging.Abstractions.NullLogger" />. </summary>
val runner: EffectRunner
val this: IButtonPusher
Task.FromResult<'TResult>(result: 'TResult) : Task<'TResult>