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>