diff --git a/LanguageExt.Core/Async/Async.Module.cs b/LanguageExt.Core/Async/Async.Module.cs new file mode 100644 index 000000000..c4abdd2a7 --- /dev/null +++ b/LanguageExt.Core/Async/Async.Module.cs @@ -0,0 +1,91 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using LanguageExt.Common; + +namespace LanguageExt; + +/// +/// The `Async` module helps transition away from the `Task` / `async` / `await` world and into one +/// where awaiting is the default setting for concurrent programming and branching/forking is the +/// thing we do the least. +/// +/// The `Async.await` function will convert a `Task` into an `A` by waiting for the `Task` to +/// complete; it will yield the thread whilst it's waiting (to play nice with other tasks in the +/// task-pool). This is just like the regular `await` keyword without all the ceremony and +/// colouring of methods. +/// +/// `Async.fork` lifts a function into an IO monad, forks it, and then runs the IO monad returning +/// a `ForkIO` object. The forked operation continues to run in parallel. The `ForkIO` object +/// contains two properties: `Await` and `Cancel` that be used to either await the result or +/// cancel the operation. +/// +/// These two functions remove the need for methods that are 'tainted' with `Task` or `async` / +/// `await` mechanics and assume that the thing we will do the most with asynchronous code is to +/// await it. +/// +/// This module shouldn't be needed too much, as the IO monad is where most of the asynchrony +/// should be. But, when converting from existing async/await code, or if you're coming from +/// language-ext v4, or earlier, where there was lots of `*Async` methods in the key types, then +/// this module will help ease the transition. +/// +public static class Async +{ + /// + /// Simple awaiter that yields the thread whilst waiting. Allows for the `Task` to + /// be used with synchronous code without blocking any threads for concurrent + /// processing. + /// + /// Task to await + /// Bound value type + /// Result of the task, `TaskCanceledException`, or any exception raised by the task + /// + public static A await(Task operation) + { + SpinWait sw = default; + while (true) + { + if (operation.IsCanceled) + { + throw new TaskCanceledException(); + } + else if (operation.IsFaulted) + { + operation.Exception.Rethrow(); + } + else if (operation.IsCompleted) + { + return operation.Result; + } + sw.SpinOnce(); + } + } + + /// + /// `Async.fork` lifts a function into an IO monad, forks it, and then runs the IO monad returning + /// the `ForkIO` object. The forked operation continues to run in parallel. The `ForkIO` object + /// contains two properties: `Await` and `Cancel` that be used to either await the result or + /// cancel the operation. + /// + /// Operation to fork + /// Optional timeout + /// Bound value type + /// The `ForkIO` object contains two properties: `Await` and `Cancel` that be used to either + /// await the result or cancel the operation. + public static ForkIO fork(Func operation, TimeSpan timeout = default) => + IO.lift(operation).ForkIO(timeout).Run(); + + /// + /// `Async.fork` lifts a function into an IO monad, forks it, and then runs the IO monad returning + /// the `ForkIO` object. The forked operation continues to run in parallel. The `ForkIO` object + /// contains two properties: `Await` and `Cancel` that be used to either await the result or + /// cancel the operation. + /// + /// Operation to fork + /// Optional timeout + /// Bound value type + /// The `ForkIO` object contains two properties: `Await` and `Cancel` that be used to either + /// await the result or cancel the operation. + public static ForkIO fork(Func> operation, TimeSpan timeout = default) => + IO.liftAsync(operation).ForkIO(timeout).Run(); +} diff --git a/LanguageExt.Core/Common/Errors.cs b/LanguageExt.Core/Common/Errors.cs index 7257b7931..55e92292b 100644 --- a/LanguageExt.Core/Common/Errors.cs +++ b/LanguageExt.Core/Common/Errors.cs @@ -152,4 +152,20 @@ public static class Errors /// End-of-stream error /// public static readonly Error EndOfStream = (EndOfStreamCode, EndOfStreamText); + + /// + /// Validation failed text + /// + public const string ValidationFailedText = + "validation failed"; + + /// + /// Validation failed code + /// + public const int ValidationFailedCode = -2000000010; + + /// + /// Validation failed error + /// + public static readonly Error ValidationFailed = (ValidationFailedCode, ValidationFailedText); } diff --git a/LanguageExt.Core/Common/Exceptions.cs b/LanguageExt.Core/Common/Exceptions.cs index 333665576..17d4c172a 100644 --- a/LanguageExt.Core/Common/Exceptions.cs +++ b/LanguageExt.Core/Common/Exceptions.cs @@ -56,4 +56,9 @@ public class Exceptions /// End-of-stream error /// public static readonly ExpectedException EndOfStream = new (Errors.EndOfStreamText, Errors.EndOfStreamCode); + + /// + /// Validation failed error + /// + public static readonly ExpectedException ValidationFailed = new (Errors.ValidationFailedText, Errors.ValidationFailedCode); } diff --git a/LanguageExt.Core/Concurrency/Atom/Atom.Metadata.cs b/LanguageExt.Core/Concurrency/Atom/Atom.Metadata.cs index 654fe9029..4af808f66 100644 --- a/LanguageExt.Core/Concurrency/Atom/Atom.Metadata.cs +++ b/LanguageExt.Core/Concurrency/Atom/Atom.Metadata.cs @@ -1,7 +1,6 @@ using System; using System.Runtime.CompilerServices; using System.Threading; -using System.Threading.Tasks; using LanguageExt.Common; using static LanguageExt.Prelude; @@ -42,11 +41,11 @@ public sealed class Atom /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - Atom(M metadata, A value, Func validator) + Atom(M metadata, A value, Func? validator) { this.value = Box.New(value); this.metadata = metadata; - this.validator = validator; + this.validator = validator ?? True; } /// @@ -74,7 +73,7 @@ internal static Atom New(M metadata, A value) => /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. + /// should be free of side effects. /// /// Function to update the atom /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded @@ -101,416 +100,40 @@ public Option Swap(Func f) sw.SpinOnce(); } } - - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(Func> f) => - lift(() => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(metadata, Box.GetValue(value)).Run(); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - if(Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - sw.SpinOnce(); - } - }); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(metadata, Box.GetValue(value)).Run(env, sta.EnvIO); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - - sw.SpinOnce(); - } - }) - select res; /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public async ValueTask> SwapAsync(Func> f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = await f(metadata, Box.GetValue(value)).ConfigureAwait(false); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. + /// should be free of side effects. /// /// Additional value to pass to `f` /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public Option Swap(X x, Func f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = f(metadata, x, Box.GetValue(value)); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its + /// IO in a success state, with the result of the invocation of `f`, if the swap succeeded and its /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Func> f) => - lift(() => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(metadata, x, Box.GetValue(value)).Run(); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - if(Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - sw.SpinOnce(); - } - }); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(metadata, x, Box.GetValue(value)).Run(env, sta.EnvIO); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - - sw.SpinOnce(); - } - }) - select res; - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public async ValueTask> SwapAsync(X x, Func> f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = await f(metadata, x, Box.GetValue(value)).ConfigureAwait(false); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public Option Swap(X x, Y y, Func f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = f(metadata, x, y, Box.GetValue(value)); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Y y, Func> f) => - lift(() => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(metadata, x, y, Box.GetValue(value)).Run(); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - if(Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - sw.SpinOnce(); - } - }); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Y y, Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(metadata, x, y, Box.GetValue(value)).Run(env, sta.EnvIO); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - - sw.SpinOnce(); - } - }) - select res; - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public async ValueTask> SwapAsync(X x, Y y, Func> f) + public IO SwapIO(Func f) => + IO.lift(_ => Swap(f) switch + { + { IsSome: true } value => (A)value, + _ => throw Exceptions.ValidationFailed + }); + + /// + /// Value accessor (read and write) + /// + /// + /// + /// * Gets will return a freshly constructed `IO` monad that can be repeatedly + /// evaluated to get the latest state of the `Atom`. + /// + /// * Sets pass an `IO` monad that will be mapped to an operation that will set + /// the value of the `Atom` each time it's evaluated. + /// + /// + public IO ValueIO { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = await f(metadata, x, y, Box.GetValue(value)).ConfigureAwait(false); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => IO.lift(_ => Value); + set => value.Bind(v => SwapIO((_, _) => v)); } /// diff --git a/LanguageExt.Core/Concurrency/Atom/Atom.cs b/LanguageExt.Core/Concurrency/Atom/Atom.cs index aa78382e6..393376c77 100644 --- a/LanguageExt.Core/Concurrency/Atom/Atom.cs +++ b/LanguageExt.Core/Concurrency/Atom/Atom.cs @@ -41,10 +41,10 @@ public sealed class Atom /// Constructor /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - Atom(A value, Func validator) + Atom(A value, Func? validator) { this.value = Box.New(value); - this.validator = validator; + this.validator = validator ?? True; } /// @@ -67,12 +67,12 @@ internal static Option> New(A value, Func validator) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static Atom New(A value) => - new Atom(value, True); + new (value, True); /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. + /// should be free of side effects. /// /// Function to update the atom /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded @@ -103,412 +103,36 @@ public Option Swap(Func f) /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. + /// should be free of side effects. /// - /// Additional value to pass to `f` /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its + /// IO in a success state, with the result of the invocation of `f`, if the swap succeeded and its /// validation passed. Failure state otherwise - public Eff SwapEff(Func> f) => - lift(() => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(Box.GetValue(value)).Run(); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - if(Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - sw.SpinOnce(); - } - }); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(Box.GetValue(value)).Run(env, sta.EnvIO); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - - sw.SpinOnce(); - } - }) - select res; - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public async ValueTask> SwapAsync(Func> f) + public IO SwapIO(Func f) => + IO.lift(_ => Swap(f) switch + { + { IsSome: true } value => (A)value, + _ => throw Exceptions.ValidationFailed + }); + + + /// + /// Value accessor (read and write) + /// + /// + /// + /// * Gets will return a freshly constructed `IO` monad that can be repeatedly + /// evaluated to get the latest state of the `Atom`. + /// + /// * Sets pass an `IO` monad that will be mapped to an operation that will set + /// the value of the `Atom` each time it's evaluated. + /// + /// + public IO ValueIO { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = await f(Box.GetValue(value)).ConfigureAwait(false); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public Option Swap(X x, Func f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = f(x, Box.GetValue(value)); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Func> f) => - lift(() => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(x, Box.GetValue(value)).Run(); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - if(Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - sw.SpinOnce(); - } - }); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(x, Box.GetValue(value)).Run(env, sta.EnvIO); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - - sw.SpinOnce(); - } - }) - select res; - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public async ValueTask> SwapAsync(X x, Func> f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = await f(x, Box.GetValue(value)).ConfigureAwait(false); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public Option Swap(X x, Y y, Func f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = f(x, y, Box.GetValue(value)); - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } - } - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Y y, Func> f) => - lift(() => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(x, y, Box.GetValue(value)).Run(); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - if(Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - sw.SpinOnce(); - } - }); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public Eff SwapEff(X x, Y y, Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueFinA = f(x, y, Box.GetValue(value)).Run(env, sta.EnvIO); - if (newValueFinA.IsFail) - { - return newValueFinA; - } - - var newValueA = newValueFinA.SuccValue; - var newValue = Box.New(newValueA); - if (!validator(newValueA)) - { - return FinFail(Error.New("Validation failed for swap")); - } - - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return newValueFinA; - } - - sw.SpinOnce(); - } - }) - select res; - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public async ValueTask> SwapAsync(X x, Y y, Func> f) - { - f = f ?? throw new ArgumentNullException(nameof(f)); - - SpinWait sw = default; - while (true) - { - var current = value; - var newValueA = await f(x, y, Box.GetValue(value)).ConfigureAwait(false); - var newValue = Box.New(newValueA); - if (!validator(Box.GetValue(newValue))) - { - return default; - } - if (Interlocked.CompareExchange(ref value, newValue, current) == current) - { - Change?.Invoke(newValueA); - return Optional(newValueA); - } - sw.SpinOnce(); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => IO.lift(_ => Value); + set => value.Bind(v => SwapIO(_ => v)); } /// diff --git a/LanguageExt.Core/Concurrency/Prelude.Concurrency.cs b/LanguageExt.Core/Concurrency/Prelude.Concurrency.cs index e99b6c947..9e9653eb5 100644 --- a/LanguageExt.Core/Concurrency/Prelude.Concurrency.cs +++ b/LanguageExt.Core/Concurrency/Prelude.Concurrency.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.CompilerServices; -using System.Threading.Tasks; namespace LanguageExt; @@ -70,21 +69,17 @@ public static partial class Prelude public static Ref Ref(A value, Func? validator = null) => STM.NewRef(value, validator); - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); @@ -92,7 +87,7 @@ public static Ref Ref(A value, Func? validator = null) => /// /// snapshot(() => x.Value = y.Value + 1); /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); @@ -111,232 +106,16 @@ public static R atomic(Func op, Isolation isolation = Isolation.Snapshot) /// If a transaction is already running, then this becomes part of the parent transaction /// /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff atomic(Eff op, Isolation isolation = Isolation.Snapshot) => - STM.DoTransaction(op, isolation); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff atomic(Eff op, Isolation isolation = Isolation.Snapshot) => - STM.DoTransaction(op, isolation); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask atomic(Func> op, Isolation isolation = Isolation.Snapshot) => - STM.DoTransaction(op, isolation); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask atomic(Func op, Isolation isolation = Isolation.Snapshot) => - STM.DoTransaction(async () => { await op().ConfigureAwait(false); return unit; }, isolation); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task atomic(Func> op, Isolation isolation = Isolation.Snapshot) => - STM.DoTransaction(async () => await op().ConfigureAwait(false), isolation).AsTask(); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task atomic(Func op, Isolation isolation = Isolation.Snapshot) => - STM.DoTransaction(async () => { await op().ConfigureAwait(false); return unit; }, isolation).AsTask(); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used /// within the transaction, then it is rolled back and retried (using the latest 'world' state). /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write /// inconsistencies. For example, if you have: /// /// var x = Ref(1); @@ -344,7 +123,7 @@ public static Task atomic(Func op, Isolation isolation = Isolation.S /// /// snapshot(() => x.Value = y.Value + 1); /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. /// Because `y` was only read-from, not written to. However, this: /// /// var x = Ref(1); @@ -357,14 +136,13 @@ public static Task atomic(Func op, Isolation isolation = Isolation.S [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Unit atomic(Action op, Isolation isolation = Isolation.Snapshot) => STM.DoTransaction(() => { op(); return unit; }, isolation); - /// /// Run the op within a new transaction /// If a transaction is already running, then this becomes part of the parent transaction /// /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// @@ -377,463 +155,76 @@ public static R snapshot(Func op) => /// If a transaction is already running, then this becomes part of the parent transaction /// /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff snapshot(Eff op) => - STM.DoTransaction(op, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff snapshot(Eff op) => - STM.DoTransaction(op, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask snapshot(Func> op) => - STM.DoTransaction(op, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask snapshot(Func op) => - STM.DoTransaction(async () => { await op().ConfigureAwait(false); return unit; }, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task snapshot(Func> op) => - STM.DoTransaction(async () => await op().ConfigureAwait(false), Isolation.Snapshot).AsTask(); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are - /// *written-to within the transaction*. If anything does write to the values used within the transaction, then - /// the transaction is rolled back and retried (using the latest 'world' state). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task snapshot(Func op) => - STM.DoTransaction(async () => { await op().ConfigureAwait(false); return unit; }, Isolation.Snapshot).AsTask(); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Snapshot isolation requires that nothing outside of the transaction has written to any of the values that are + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are /// *written-to within the transaction*. If anything does write to the values used within the transaction, then /// the transaction is rolled back and retried (using the latest 'world' state). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Unit snapshot(Action op) => - STM.DoTransaction(() => { op(); return unit; }, Isolation.Snapshot); - - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static R serial(Func op) => - STM.DoTransaction(op, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff serial(Eff op) => - STM.DoTransaction(op, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff serial(Eff op) => - STM.DoTransaction(op, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask serial(Func> op) => - STM.DoTransaction(op, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask serial(Func op) => - STM.DoTransaction(async () => { await op().ConfigureAwait(false); return unit; }, Isolation.Snapshot); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task serial(Func> op) => - STM.DoTransaction(async () => await op().ConfigureAwait(false), Isolation.Snapshot).AsTask(); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Task serial(Func op) => - STM.DoTransaction(async () => { await op().ConfigureAwait(false); return unit; }, Isolation.Snapshot).AsTask(); - - /// - /// Run the op within a new transaction - /// If a transaction is already running, then this becomes part of the parent transaction - /// - /// - /// Serialisable isolation requires that nothing outside of the transaction has written to any of the values that - /// are *read-from or written-to within the transaction*. If anything does write to the values that are used - /// within the transaction, then it is rolled back and retried (using the latest 'world' state). - /// - /// It is the most strict form of isolation, and the most likely to conflict; but protects against cross read/write - /// inconsistencies. For example, if you have: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// snapshot(() => x.Value = y.Value + 1); - /// - /// Then something writing to `y` mid-way through the transaction would not cause the transaction to fail. - /// Because `y` was only read-from, not written to. However, this: - /// - /// var x = Ref(1); - /// var y = Ref(2); - /// - /// serial(() => x.Value = y.Value + 1); - /// - /// ... would fail if something wrote to `y`. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Unit serial(Action op) => - STM.DoTransaction(() => { op(); return unit; }, Isolation.Snapshot); - - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A swap(Ref r, Func f) => - r.Swap(f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask swapAsync(Ref r, Func> f) => - r.SwapAsync(f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff swapEff(Ref r, Func> f) => - r.SwapEff(f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff swapEff(Ref r, Func> f) => - r.SwapEff(f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A swap(Ref r, X x, Func f) => - r.Swap(x, f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask swapAsync(Ref r, X x, Func> f) => - r.SwapAsync(x, f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff swapEff(Ref r, X x, Func> f) => - r.SwapEff(x, f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff swapEff(Ref r, X x, Func> f) => - r.SwapEff(x, f); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A swap(Ref r, X x, Y y, Func f) => - r.Swap(x, y, f); - + public static Unit snapshot(Action op) => + STM.DoTransaction(() => { op(); return unit; }, Isolation.Snapshot); + /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction + /// Run the op within a new transaction + /// If a transaction is already running, then this becomes part of the parent transaction /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` + /// + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used + /// within the transaction, then it is rolled back and retried (using the latest 'world' state). + /// + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write + /// inconsistencies. For example, if you have: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// snapshot(() => x.Value = y.Value + 1); + /// + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. + /// Because `y` was only read-from, not written to. However, this: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// serial(() => x.Value = y.Value + 1); + /// + /// ... would fail if something wrote to `y`. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ValueTask swapAsync(Ref r, X x, Y y, Func> f) => - r.SwapAsync(x, y, f); + public static R serial(Func op) => + STM.DoTransaction(op, Isolation.Snapshot); /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction + /// Run the op within a new transaction + /// If a transaction is already running, then this becomes part of the parent transaction /// - /// `Ref` to process - /// Function to update the `Ref` - /// The value returned from `f` + /// + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used + /// within the transaction, then it is rolled back and retried (using the latest 'world' state). + /// + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write + /// inconsistencies. For example, if you have: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// snapshot(() => x.Value = y.Value + 1); + /// + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. + /// Because `y` was only read-from, not written to. However, this: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// serial(() => x.Value = y.Value + 1); + /// + /// ... would fail if something wrote to `y`. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff swapEff(Ref r, X x, Y y, Func> f) => - r.SwapEff(x, y, f); - + public static Unit serial(Action op) => + STM.DoTransaction(() => { op(); return unit; }, Isolation.Snapshot); + /// /// Swap the old value for the new returned by `f` /// Must be run within a `sync` transaction @@ -842,9 +233,9 @@ public static Eff swapEff(Ref r, X x, Y y, Func> /// Function to update the `Ref` /// The value returned from `f` [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Eff swapEff(Ref r, X x, Y y, Func> f) => - r.SwapEff(x, y, f); - + public static A swap(Ref r, Func f) => + r.Swap(f); + /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: @@ -867,50 +258,6 @@ public static Eff swapEff(Ref r, X x, Y y, Func(Ref r, Func f) => r.Commute(f); - /// - /// Must be called in a transaction. Sets the in-transaction-value of - /// ref to: - /// - /// `f(in-transaction-value-of-ref)` - /// - /// and returns the in-transaction-value when complete. - /// - /// At the commit point of the transaction, `f` is run *AGAIN* with the - /// most recently committed value: - /// - /// `f(most-recently-committed-value-of-ref)` - /// - /// Thus `f` should be commutative, or, failing that, you must accept - /// last-one-in-wins behavior. - /// - /// Commute allows for more concurrency than just setting the Ref's value - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A commute(Ref r, X x, Func f) => - r.Commute(x, f); - - /// - /// Must be called in a transaction. Sets the in-transaction-value of - /// ref to: - /// - /// `f(in-transaction-value-of-ref)` - /// - /// and returns the in-transaction-value when complete. - /// - /// At the commit point of the transaction, `f` is run *AGAIN* with the - /// most recently committed value: - /// - /// `f(most-recently-committed-value-of-ref)` - /// - /// Thus `f` should be commutative, or, failing that, you must accept - /// last-one-in-wins behavior. - /// - /// Commute allows for more concurrency than just setting the Ref's value - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static A commute(Ref r, X x, Y y, Func f) => - r.Commute(x, y, f); - /// /// Atoms provide a way to manage shared, synchronous, independent state without /// locks. @@ -1034,289 +381,22 @@ public static Option> Atom(M metadata, A value, Func v /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. + /// should be free of side effects. /// /// Function to update the atom /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded /// and its validation passed. None otherwise public static Option swap(Atom ma, Func f) => ma.Swap(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, Func> f) => - ma.SwapEff(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, Func> f) => - ma.SwapEff(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static ValueTask> swapAsync(Atom ma, Func> f) => - ma.SwapAsync(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static Option swap(Atom ma, X x, Func f) => - ma.Swap(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Func> f) => - ma.SwapEff(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Func> f) => - ma.SwapEff(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static ValueTask> swapAsync(Atom ma, X x, Func> f) => - ma.SwapAsync(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static Option swap(Atom ma, X x, Y y, Func f) => - ma.Swap(x, y, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Y y, Func> f) => - ma.SwapEff(x, y, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Y y, Func> f) => - ma.SwapEff(x, y, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static ValueTask> swapAsync(Atom ma, X x, Y y, Func> f) => - ma.SwapAsync(x, y, f); - + /// /// Atomically updates the value by passing the old value to `f` and updating /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. + /// should be free of side effects. /// /// Function to update the atom /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded /// and its validation passed. None otherwise public static Option swap(Atom ma, Func f) => ma.Swap(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, Func> f) => - ma.SwapEff(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, Func> f) => - ma.SwapEff(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static ValueTask> swapAsync(Atom ma, Func> f) => - ma.SwapAsync(f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static Option swap(Atom ma, X x, Func f) => - ma.Swap(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Func> f) => - ma.SwapEff(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Func> f) => - ma.SwapEff(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static ValueTask> swapAsync(Atom ma, X x, Func> f) => - ma.SwapAsync(x, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static Option swap(Atom ma, X x, Y y, Func f) => - ma.Swap(x, y, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Y y, Func> f) => - ma.SwapEff(x, y, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Eff in a Succ state, with the result of the invocation of `f`, if the swap succeeded and its - /// validation passed. Failure state otherwise - public static Eff swapEff(Atom ma, X x, Y y, Func> f) => - ma.SwapEff(x, y, f); - - /// - /// Atomically updates the value by passing the old value to `f` and updating - /// the atom with the result. Note: `f` may be called multiple times, so it - /// should be free of side-effects. - /// - /// Additional value to pass to `f` - /// Additional value to pass to `f` - /// Function to update the atom - /// Option in a Some state, with the result of the invocation of `f`, if the swap succeeded - /// and its validation passed. None otherwise - public static ValueTask> swapAsync(Atom ma, X x, Y y, Func> f) => - ma.SwapAsync(x, y, f); } diff --git a/LanguageExt.Core/Concurrency/STM/CommuteRef.cs b/LanguageExt.Core/Concurrency/STM/CommuteRef.cs index efae3c6f1..9294037ba 100644 --- a/LanguageExt.Core/Concurrency/STM/CommuteRef.cs +++ b/LanguageExt.Core/Concurrency/STM/CommuteRef.cs @@ -26,9 +26,6 @@ public A Value public override bool Equals(object? obj) => obj is A val && Equals(val); public bool Equals(A other) => EqDefault.Equals(other, Value); public A Swap(Func f) => Ref.Swap(f); - public A Swap(X x, Func f) => Ref.Swap(x, f); - public A Swap(X x, Y y, Func f) => Ref.Swap(x, y, f); + public IO SwapIO(Func f) => Ref.SwapIO(f); public CommuteRef Commute(Func f) => Ref.Commute(f); - public A Commute(X x, Func f) => Ref.Commute(x, f); - public A Commute(X x, Y y, Func f) => Ref.Commute(x, y, f); } diff --git a/LanguageExt.Core/Concurrency/STM/Ref.cs b/LanguageExt.Core/Concurrency/STM/Ref.cs index b3b88e09d..a728ad604 100644 --- a/LanguageExt.Core/Concurrency/STM/Ref.cs +++ b/LanguageExt.Core/Concurrency/STM/Ref.cs @@ -36,7 +36,7 @@ internal void OnChange(A value) => Change?.Invoke(value); /// - /// Value accessor + /// Value accessor (read and write) /// public A Value { @@ -44,6 +44,28 @@ public A Value set => STM.Write(Id, value!); } + /// + /// Value accessor (read and write) + /// + /// + /// + /// * Gets will return a freshly constructed `IO` monad that can be repeatedly + /// evaluated to get the latest state of the `Ref`. + /// + /// * Sets pass an `IO` monad that will be mapped to an operation that will set + /// the value of the `Ref` each time it's evaluated. + /// + /// + public IO ValueIO + { + get => IO.lift(_ => (A)STM.Read(Id)); + set => value.Map(v => + { + STM.Write(Id, v!); + return unit; + }); + } + /// /// Implicit conversion operator /// @@ -94,164 +116,14 @@ public A Swap(Func f) /// /// Swap function /// The value returned from `f` - public async ValueTask SwapAsync(Func> f) - { - var v = await f(Value).ConfigureAwait(false); - Value = v; - return v; - } - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public Eff SwapEff(Func> f) => - lift(() => - { - var fv = f(Value).Run(); - if (fv.IsFail) return fv; - Value = fv.SuccValue; - return fv; - }); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public Eff SwapEff(Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - var fv = f(Value).Run(env, sta.EnvIO); - if (fv.IsFail) return fv; - Value = fv.SuccValue; - return fv; - }) - select res; - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public A Swap(X x, Func f) - { - var v = f(x, Value); - Value = v; - return v; - } - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public async ValueTask SwapAsync(X x, Y y, Func> f) - { - var v = await f(x, y, Value).ConfigureAwait(false); - Value = v; - return v; - } - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public Eff SwapEff(X x, Y y, Func> f) => - lift(() => - { - var fv = f(x, y, Value).Run(); - if (fv.IsFail) return fv; - Value = fv.SuccValue; - return fv; - }); - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public Eff SwapEff(X x, Y y, Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - var fv = f(x, y, Value).Run(env, sta.EnvIO); - if (fv.IsFail) return fv; - Value = fv.SuccValue; - return fv; - }) - select res; - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public A Swap(X x, Y y, Func f) - { - var v = f(x, y, Value); - Value = v; - return v; - } - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public async ValueTask SwapAsync(X x, Func> f) - { - var v = await f(x, Value).ConfigureAwait(false); - Value = v; - return v; - } - - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public Eff SwapEff(X x, Func> f) => - lift(() => + public IO SwapIO(Func f) => + IO.lift(_ => { - var fv = f(x, Value).Run(); - if (fv.IsFail) return fv; - Value = fv.SuccValue; + var fv = f(Value); + Value = fv; return fv; }); - /// - /// Swap the old value for the new returned by `f` - /// Must be run within a `sync` transaction - /// - /// Swap function - /// The value returned from `f` - public Eff SwapEff(X x, Func> f) => - from sta in getState() - from res in Eff.Lift( - env => - { - var fv = f(x, Value).Run(env, sta.EnvIO); - if (fv.IsFail) return fv; - Value = fv.SuccValue; - return fv; - }) - select res; - /// /// Must be called in a transaction. Sets the in-transaction-value of /// ref to: @@ -275,52 +147,4 @@ public CommuteRef Commute(Func f) STM.Commute(Id, f); return new CommuteRef(this); } - - /// - /// Must be called in a transaction. Sets the in-transaction-value of - /// ref to: - /// - /// `f(in-transaction-value-of-ref)` - /// - /// and returns the in-transaction-value when complete. - /// - /// At the commit point of the transaction, `f` is run *AGAIN* with the - /// most recently committed value: - /// - /// `f(most-recently-committed-value-of-ref)` - /// - /// Thus `f` should be commutative, or, failing that, you must accept - /// last-one-in-wins behavior. - /// - /// Commute allows for more concurrency than just setting the Ref's value - /// - public CommuteRef Commute(X x, Func f) - { - STM.Commute(Id, x, f); - return new CommuteRef(this); - } - - /// - /// Must be called in a transaction. Sets the in-transaction-value of - /// ref to: - /// - /// `f(in-transaction-value-of-ref)` - /// - /// and returns the in-transaction-value when complete. - /// - /// At the commit point of the transaction, `f` is run *AGAIN* with the - /// most recently committed value: - /// - /// `f(most-recently-committed-value-of-ref)` - /// - /// Thus `f` should be commutative, or, failing that, you must accept - /// last-one-in-wins behavior. - /// - /// Commute allows for more concurrency than just setting the Ref's value - /// - public CommuteRef Commute(X x, Y y, Func f) - { - STM.Commute(Id, x, y, f); - return new CommuteRef(this); - } } diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Catch/IOCatch.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Catch/IOCatch.cs deleted file mode 100644 index a6d73e0aa..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Catch/IOCatch.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using LanguageExt.Effects; -using static LanguageExt.Prelude; - -namespace LanguageExt; - -public readonly struct IOCatch(Func> fail) -{ - public IOCatch(Func predicate, Func> fail) : - this(e => predicate(e) ? fail(e) : Fail(e)) - { } - - public IOCatch, E, A> As => - new (Fail); - - [Pure, MethodImpl(Opt.Default)] - public IO Run(E error) => - fail(error); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(CatchValue ma, IOCatch mb) => - new (e => ma.Match(e) ? Pure(ma.Value(e)) : mb.Run(e)); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(CatchError ma, IOCatch mb) => - new (e => ma.Match(e) ? Fail(ma.Value(e)) : mb.Run(e)); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(IOCatch ma, CatchValue mb) => - new (e => - ma.Run(e) - .Match>( - Succ: Pure, - Fail: e1 => mb.Match(e1) - ? Pure(mb.Value(e1)) - : Fail(e1)).Flatten()); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(IOCatch ma, CatchError mb) => - new (e => - ma.Run(e) - .Match>( - Succ: Pure, - Fail: e1 => mb.Match(e1) - ? Fail(mb.Value(e1)) - : Fail(e1)).Flatten()); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(IOCatch ma, IOCatch mb) => - new (e => ma.Run(e) | mb.Run(e)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Apply.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Apply.cs deleted file mode 100644 index b9bb0b57e..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Apply.cs +++ /dev/null @@ -1,223 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; -using static LanguageExt.Prelude; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma) => - new(mf.Morphism.Apply(ma.Morphism)); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma, - IO mb) => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this IO> mf, - IO ma) => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma, - IO mb, - IO mc) => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this IO> mf, - IO ma, - IO mb) => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> Apply( - this IO> mf, - IO ma) => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma, - IO mb, - IO mc, - IO md) => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc).Apply(md); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this IO> mf, - IO ma, - IO mb, - IO mc) => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> Apply( - this IO> mf, - IO ma, - IO mb) => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>>> Apply( - this IO> mf, - IO ma) => - mf.Map(curry).Apply(ma); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Non-lifted functions - // - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this Func f, - IO ma) => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this Func f, - IO ma, - IO mb) => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this Func f, - IO ma) => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this Func f, - IO ma, - IO mb, - IO mc) => - IO>.Pure(f).Apply(ma, mb, mc); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this Func f, - IO ma, - IO mb) => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> Apply( - this Func f, - IO ma) => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this Func f, - IO ma, - IO mb, - IO mc, - IO md) => - IO>.Pure(f).Apply(ma, mb, mc, md); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this Func f, - IO ma, - IO mb, - IO mc) => - IO>.Pure(f).Apply(ma, mb, mc); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> Apply( - this Func f, - IO ma, - IO mb) => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>>> Apply( - this Func f, - IO ma) => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative - /// - public static IO Action( - this IO ma, - IO mb) => - new(ma.Morphism.Action(mb.Morphism)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Catch.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Catch.cs deleted file mode 100644 index 453eff3c9..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Catch.cs +++ /dev/null @@ -1,42 +0,0 @@ -/* -using System; -using static LanguageExt.Prelude; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Catch an error if the error matches the argument provided - /// - public static IO Catch(this IO ma, E Error, Func> Fail) => - ma | @catch(Error, Fail); - - /// - /// Catch an error if the error matches the argument provided - /// - public static IO Catch(this IO ma, E Error, IO Fail) => - ma | @catch(Error, Fail); - - /// - /// Catch all errors - /// - public static IO Catch(this IO ma, IO Fail) => - ma | @catch(Fail); - - /// - /// Catch all errors - /// - public static IO Catch(this IO ma, Func> Fail) => - ma | @catch(Fail); - - /// - /// Catch errors - /// - public static IO CatchOf( - this IO ma, - Func> Fail) - where MATCH_ERROR : E => - ma | @catchOf(Fail); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Repeat.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Repeat.cs deleted file mode 100644 index a50077781..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Repeat.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* -using System; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Keeps repeating the computation - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO Repeat(this IO ma) => - new(Transducer.repeat(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps repeating the computation, until the scheduler expires - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO Repeat(this IO ma, Schedule schedule) => - new(Transducer.repeat(schedule, ma.Morphism)); - - /// - /// Keeps repeating the computation until the predicate returns false - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatWhile(this IO ma, Func predicate) => - new(Transducer.repeatWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatWhile( - this IO ma, - Schedule schedule, - Func predicate) => - new(Transducer.repeatWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation until the predicate returns true - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatUntil( - this IO ma, - Func predicate) => - new(Transducer.repeatUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatUntil( - this IO ma, - Schedule schedule, - Func predicate) => - new(Transducer.repeatUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Retry.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Retry.cs deleted file mode 100644 index 24767ab39..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.Retry.cs +++ /dev/null @@ -1,85 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Keeps retrying the computation - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO Retry( - this IO ma) => - new(Transducer.retry(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps retrying the computation, until the scheduler expires - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO Retry( - this IO ma, - Schedule schedule) => - new(Transducer.retry(schedule, ma.Morphism)); - - /// - /// Keeps retrying the computation until the predicate returns false - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryWhile( - this IO ma, - Func predicate) => - new(Transducer.retryWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryWhile( - this IO ma, - Schedule schedule, - Func predicate) => - new(Transducer.retryWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation until the predicate returns true - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryUntil( - this IO ma, - Func predicate) => - new(Transducer.retryUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryUntil( - this IO ma, - Schedule schedule, - Func predicate) => - new(Transducer.retryUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.cs deleted file mode 100644 index 4899e1b9f..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Extensions/IO.Extensions.cs +++ /dev/null @@ -1,216 +0,0 @@ -/* -using System; -using LanguageExt.Effects; -using LanguageExt.Traits; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - public static IO As(this K, A> ma) => - (IO)ma; - - /// - /// Lift transducer into an effect monad - /// - public static IO ToIO(this Transducer> t) => - new (t); - - /// - /// Lift transducer in an effect monad - /// - public static IO ToIO(this Transducer t) => - new(t); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic join - // - - /// - /// Monadic join operator - /// - /// - /// Collapses a nested IO monad so there is no nesting. - /// - /// Nest IO monad to flatten - /// Runtime - /// Error type - /// Bound value - /// Flattened IO monad - public static IO Flatten(this IO> mma) => - mma.Bind(ma => ma); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Binding extensions - // - - public static IO Bind(this Transducer ma, Func> f) => - IO.Lift(Transducer.compose(Transducer.constant, Unit>(default), ma.Morphism)).Bind(f); - - public static IO Bind(this Transducer> ma, Func> f) => - IO.Lift(Transducer.compose(Transducer.constant, Unit>(default), ma.Morphism)).Bind(f); - - public static IO Bind(this Transducer, A> ma, Func> f) => - IO.Lift(ma.Morphism).Bind(f); - - public static IO Bind(this Transducer, Sum> ma, Func> f) => - IO.Lift(ma.Morphism).Bind(f); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // SelectMany extensions - // - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this (IO First, IO Second) self, - Func<(A First, B Second), IO> bind, - Func<(A First, B Second), C, D> project) => - self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))); - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this IO self, - Func First, IO Second)> bind, - Func project) => - self.Bind(a => bind(a).Zip().Map(cd => project(a, cd))); - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this (IO First, IO Second, IO Third) self, - Func<(A First, B Second, C Third), IO> bind, - Func<(A First, B Second, C Third), D, E> project) => - self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))); - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this IO self, - Func First, IO Second, IO Third)> bind, - Func project) => - self.Bind(a => bind(a).Zip().Map(cd => project(a, cd))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer ma, - Func> bind, - Func project) => - IO.Lift(Transducer.compose(Transducer.constant, Unit>(default), ma)) - .Bind(x => bind(x).Map(y => project(x, y))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer, A> ma, - Func> bind, - Func project) => - IO.Lift(ma).Bind(x => bind(x).Map(y => project(x, y))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer> ma, - Func> bind, - Func project) => - IO.Lift(Transducer.compose(Transducer.constant, Unit>(default), ma)) - .Bind(x => bind(x).Map(y => project(x, y))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer, Sum> ma, - Func> bind, - Func project) => - IO.Lift(ma).Bind(x => bind(x).Map(y => project(x, y))); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Zipping - // - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - public static IO Zip( - this (IO First, IO Second) tuple) => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - public static IO Zip( - this (IO First, - IO Second, - IO Third) tuple) => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism, tuple.Third.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - public static IO Zip( - this IO First, - IO Second) => - (First, Second).Zip(); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - public static IO Zip( - this IO First, - IO Second, - IO Third) => - (First, Second, Third).Zip(); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/ForkIO.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/ForkIO.cs deleted file mode 100644 index 2ac517ba6..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/ForkIO.cs +++ /dev/null @@ -1,18 +0,0 @@ -/* -#nullable enable -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -/// -/// Result of forking an IO monad -/// -/// An IO monad, which if invoked, would cancel the forked IO operation -/// An IO monad, which if invoked, would attempt to get the result of the forked IO operation -/// Bound value type -public readonly record struct ForkIO( - IO Cancel, - IO Await) - where RT : HasIO; - -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/IO.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/IO.cs deleted file mode 100644 index 2840a72bb..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/IO.cs +++ /dev/null @@ -1,1359 +0,0 @@ -/* -using System; -using LanguageExt.Common; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using LanguageExt.Effects; -using LanguageExt.Effects.Traits; -using LanguageExt.Traits; - -namespace LanguageExt; - -/// -/// Transducer based IO monad -/// -/// Runtime struct -/// Error value type -/// Bound value type -public readonly struct IO : K, A> -{ - /// - /// Underlying transducer that captures all of the IO behaviour - /// - readonly IO, E, A> effect; - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Constructors - // - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - internal IO(IO, E, A> effect) => - this.effect = effect; - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - internal IO(Transducer, Sum> thunk) => - effect = new IO, E, A>(thunk); - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func, Sum> thunk) - : this(Transducer.lift(thunk)) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func, A> thunk) - : this(rt => Sum.Right(thunk(rt))) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Transducer, A> thunk) - : this(Transducer.compose(thunk, Transducer.mkRight())) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func, Either> thunk) - : this(rt => thunk(rt).ToSum()) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Transducer, Either> thunk) - : this(Transducer.compose(thunk, Transducer.lift, Sum>(x => x.ToSum()))) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - internal IO(Transducer> thunk) - : this (Transducer.compose(Transducer.constant, Unit>(default), thunk)) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func> thunk) - : this (Transducer.lift(thunk)) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func thunk) - : this(_ => Sum.Right(thunk())) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Transducer thunk) - : this(Transducer.compose(thunk, Transducer.mkRight())) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func> thunk) - : this(_ => thunk().ToSum()) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Transducer> thunk) - : this(Transducer.compose(thunk, Transducer.lift, Sum>(x => x.ToSum()))) - { } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Transducer - // - - /// - /// Access to the underlying transducer - /// - [Pure] - public Transducer, Sum> Morphism => - effect.Morphism; - - /// - /// Reduction of the underlying transducer - /// - /// Reducer - /// - /// - [Pure, MethodImpl(Opt.Default)] - public Reducer, S> Transform(Reducer, S> reduce) => - Morphism.Transform(reduce); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Invoking - // - - /// - /// All IO monads will catch exceptions at their point of invocation (i.e. in the `Run*` methods). - /// But they do not catch exceptions elsewhere without explicitly using this `Try()` method. - /// - /// This wraps the `IO` monad in a try/catch that converts exceptional errors to an `E` and - /// therefore puts the `IO` in a `Fail` state the expression that can then be matched upon. - /// - /// - /// Useful when you want exceptions to be dealt with through matching/bi-mapping/etc. - /// and not merely be caught be the exception handler in `Run*`. - /// - /// - /// This is used automatically for the first argument in the coalescing `|` operator so that any - /// exceptional failures, in the first argument, allow the second argument to be invoked. - /// - [Pure, MethodImpl(Opt.Default)] - public IO Try() => - new(effect.Try()); - - /// - /// Invoke the effect - /// - [Pure, MethodImpl(Opt.Default)] - public Either Run(MinRT env) => - effect.Run(env); - - /// - /// Invoke the effect - /// - [Pure, MethodImpl(Opt.Default)] - public Either Run() => - Run(new MinRT()); - - /// - /// Invoke the effect (which could produce multiple values). Run a reducer for - /// each value yielded. - /// - [Pure, MethodImpl(Opt.Default)] - public Fin RunMany(MinRT env, S initialState, Func, TResult> reducer) => - effect.RunMany(env, initialState, reducer); - - /// - /// Invoke the effect (which could produce multiple values). Run a reducer for - /// each value yielded. - /// - [Pure, MethodImpl(Opt.Default)] - public Fin RunMany(S initialState, Func, TResult> reducer) => - effect.RunMany(new MinRT(), initialState, reducer); - - /// - /// Invoke the effect (which could produce multiple values). Collect those results in a `Seq` - /// - [Pure, MethodImpl(Opt.Default)] - public Either> RunMany(MinRT env) => - effect.RunMany(env); - - /// - /// Invoke the effect (which could produce multiple values). Collect those results in a `Seq` - /// - [Pure, MethodImpl(Opt.Default)] - public Either> RunMany() => - effect.RunMany(new MinRT()); - - /// - /// Invoke the effect asynchronously - /// - [Pure, MethodImpl(Opt.Default)] - public Task> RunAsync(MinRT env) => - effect.RunAsync(env); - - /// - /// Invoke the effect (which could produce multiple values). Run a reducer for - /// each value yielded. - /// - [Pure, MethodImpl(Opt.Default)] - public Task> RunManyAsync(MinRT env, S initialState, Func, TResult> reducer) => - effect.RunManyAsync(env, initialState, reducer); - - /// - /// Invoke the effect (which could produce multiple values). Run a reducer for - /// each value yielded. - /// - [Pure, MethodImpl(Opt.Default)] - public Task> RunManyAsync(S initialState, Func, TResult> reducer) => - effect.RunManyAsync(new MinRT(), initialState, reducer); - - /// - /// Invoke the effect (which could produce multiple values). Collect those results in a `Seq` - /// - [Pure, MethodImpl(Opt.Default)] - public Task>> RunManyAsync(MinRT env) => - effect.RunManyAsync(env); - - /// - /// Invoke the effect (which could produce multiple values). Collect those results in a `Seq` - /// - [Pure, MethodImpl(Opt.Default)] - public Task>> RunManyAsync() => - effect.RunManyAsync(new MinRT()); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Timeout - // - - /// - /// Cancel the operation if it takes too long - /// - /// Timeout period - /// An IO operation that will timeout if it takes too long - [Pure, MethodImpl(Opt.Default)] - public IO Timeout(TimeSpan timeoutDelay) => - new(effect.Timeout(timeoutDelay)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Lifting - // - - /// - /// Lift a value into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Pure(A value) => - new (_ => Sum.Right(value)); - - /// - /// Lift a failure into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Fail(E error) => - new (_ => Sum.Left(error)); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func, Either> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer, Either> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func, Sum> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer, Sum> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func, A> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer, A> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer f) => - new (f); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO LiftIO(Func, Task> f) => - new(IO, E, A>.LiftIO(f)); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO LiftIO(Func, Task>> f) => - new(IO, E, A>.LiftIO(f)); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO LiftIO(Func, Task>> f) => - new(IO, E, A>.LiftIO(f)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Memoisation and tail calls - // - - /// - /// Memoise the result, so subsequent calls don't invoke the side-effect - /// - [Pure, MethodImpl(Opt.Default)] - public IO Memo() => - new(effect.Memo()); - - /// - /// Wrap this around the final `from` call in a `IO` LINQ expression to void a recursion induced space-leak. - /// - /// - /// - /// IO recursive(int x) => - /// from x in writeLine(x) - /// from r in tail(recursive(x + 1)) - /// select r; <--- this never runs - /// - /// - /// - /// This means the result of the LINQ expression comes from the final `from`, _not_ the `select. If the - /// type of the `final` from differs from the type of the `select` then this has no effect. - /// - /// - /// Background: When making recursive LINQ expressions, the final `select` is problematic because it means - /// there's code to run _after_ the final `from` expression. This means there's you're guaranteed to have a - /// space-leak due to the need to hold thunks to the final `select` on every recursive step. - /// - /// This function ignores the `select` altogether and says that the final `from` is where we get our return - /// result from and therefore there's no need to hold the thunk. - /// - /// IO operation that's marked ready for tail recursion - [Pure, MethodImpl(Opt.Default)] - public IO Tail() => - new(effect.Tail()); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Forking - // - - /// - /// Queue this IO operation to run on the thread-pool. - /// - /// Maximum time that the forked IO operation can run for. `None` for no timeout. - /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel - /// the forked IO operation or to await the result of it. - /// - [MethodImpl(Opt.Default)] - public IO> Fork(Option timeout = default) => - new(Transducer.fork(Morphism, timeout).Map(TFork.ToIO)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Map and map-left - // - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO Map(Func f) => - new(effect.Map(f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO Select(Func f) => - new(effect.Map(f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO Map(Transducer f) => - new(effect.Map(f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO MapFail(Func f) => - new(effect.MapFail(f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO MapFail(Transducer f) => - new(effect.MapFail(f)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Bi-map - // - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO BiMap(Func Succ, Func Fail) => - new(effect.BiMap(Succ, Fail)); - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO BiMap(Transducer Succ, Transducer Fail) => - new(effect.BiMap(Succ, Fail)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Matching - // - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure] - public IO Match(Func Succ, Func Fail) => - new(effect.Match(Succ, Fail)); - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure] - public IO Match(Transducer Succ, Transducer Fail) => - new(effect.Match(Succ, Fail)); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public IO IfFail(Func Fail) => - new(effect.IfFail(Fail)); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public IO IfFail(Transducer Fail) => - new(effect.IfFail(Fail)); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public IO IfFail(A Fail) => - new(effect.IfFail(Fail)); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public IO IfFailIO(Func> Fail) => - new(Transducer.bimap( - Morphism, - e => Fail(e).Morphism, - x => Transducer.constant, Sum>(Sum.Right(x))) - .Flatten()); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public IO IfFailIO(IO Fail) => - IfFailIO(_ => Fail); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Filter - // - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - [Pure, MethodImpl(Opt.Default)] - public IO Filter(Func predicate) => - new(effect.Filter(predicate)); - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - [Pure, MethodImpl(Opt.Default)] - public IO Filter(Transducer predicate) => - new(effect.Filter(predicate)); - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - [Pure, MethodImpl(Opt.Default)] - public IO Where(Func predicate) => - new(effect.Filter(predicate)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic binding - // - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO Bind(Func> f) => - new(Transducer.bind(Morphism, x => f(x).Morphism)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO Bind(Func, B>> f) => - new(Transducer.bind(Morphism, x => f(x).As().Morphism)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// transducer provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the transducer provided - [Pure, MethodImpl(Opt.Default)] - public IO Bind(Transducer> f) => - Map(f).Flatten(); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO Bind(Func> f) => - new(effect.Bind(f)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO Bind(Func> f) => - new(effect.Bind(f)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO Bind(Func> f) => - new(effect.Bind(f)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO Bind(Func, B>> f) => - new(effect.Bind(f)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic binding and projection - // - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO SelectMany(Func> bind, Func project) => - new(Transducer.selectMany(Morphism, x => bind(x).Morphism, project)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO SelectMany(Func> bind, Func project) => - new(effect.SelectMany(bind, project)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO SelectMany(Func> bind, Func project) => - new(effect.SelectMany(bind, project)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO SelectMany(Func> bind, Func project) => - new(effect.SelectMany(bind, project)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO SelectMany(Func, Unit>> bind, Func project) => - new(effect.SelectMany(bind, project)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO SelectMany(Func> bind, Func project) => - new(effect.SelectMany(bind, project)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - [Pure, MethodImpl(Opt.Default)] - public IO SelectMany(Func, B>> bind, Func project) => - new(effect.SelectMany(bind, project)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Folding - // - - /// - /// Fold the effect forever or until the schedule expires - /// - [Pure, MethodImpl(Opt.Default)] - public IO Fold( - Schedule schedule, - S initialState, - Func folder) => - new(effect.Fold(schedule, initialState, folder)); - - /// - /// Fold the effect forever - /// - [Pure, MethodImpl(Opt.Default)] - public IO Fold( - S initialState, - Func folder) => - new(effect.Fold(initialState, folder)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - Schedule schedule, - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - new(effect.FoldUntil(schedule, initialState, folder, predicate)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - Schedule schedule, - S initialState, - Func folder, - Func stateIs) => - new(effect.FoldUntil(schedule, initialState, folder, stateIs)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - Schedule schedule, - S initialState, - Func folder, - Func valueIs) => - new(effect.FoldUntil(schedule, initialState, folder, valueIs)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - new(effect.FoldUntil(initialState, folder, predicate)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - S initialState, - Func folder, - Func stateIs) => - new(effect.FoldUntil(initialState, folder, stateIs)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - S initialState, - Func folder, - Func valueIs) => - new(effect.FoldUntil(initialState, folder, valueIs)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - Schedule schedule, - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - new(effect.FoldWhile(schedule, initialState, folder, predicate)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - Schedule schedule, - S initialState, - Func folder, - Func stateIs) => - new(effect.FoldWhile(schedule, initialState, folder, stateIs)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - Schedule schedule, - S initialState, - Func folder, - Func valueIs) => - new(effect.FoldWhile(schedule, initialState, folder, valueIs)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - new(effect.FoldWhile(initialState, folder, predicate)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - S initialState, - Func folder, - Func stateIs) => - new(effect.FoldWhile(initialState, folder, stateIs)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - S initialState, - Func folder, - Func valueIs) => - new(effect.FoldWhile(initialState, folder, valueIs)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Folding, where each fold is monadic - // - - /// - /// Fold the effect forever or until the schedule expires - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldM( - Schedule schedule, - S initialState, - Func> folder) => - new(effect.FoldM(schedule, initialState, (s, x) => folder(s, x).Morphism)); - - /// - /// Fold the effect forever - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldM( - S initialState, - Func> folder) => - new(effect.FoldM(initialState, (s, x) => folder(s, x).Morphism)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntilM( - Schedule schedule, - S initialState, - Func> folder, - Func valueIs) => - new(effect.FoldUntilM(schedule, initialState, (s, x) => folder(s, x).Morphism, valueIs)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntilM( - S initialState, - Func> folder, - Func valueIs) => - new(effect.FoldUntilM(initialState, (s, x) => folder(s, x).Morphism, valueIs)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhileM( - Schedule schedule, - S initialState, - Func> folder, - Func valueIs) => - new(effect.FoldWhileM(schedule, initialState, (s, x) => folder(s, x).Morphism, valueIs)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhileM( - S initialState, - Func> folder, - Func valueIs) => - new(effect.FoldWhileM(initialState, (s, x) => folder(s, x).Morphism, valueIs)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Synchronisation between contexts - // - - /// - /// Make a transducer run on the `SynchronizationContext` that was captured at the start - /// of an `Invoke` call. - /// - /// - /// The transducer receives its input value from the currently running sync-context and - /// then proceeds to run its operation in the captured `SynchronizationContext`: - /// typically a UI context, but could be any captured context. The result of the - /// transducer is the received back on the currently running sync-context. - /// - /// Transducer - /// - [Pure, MethodImpl(Opt.Default)] - public IO Post() => - new(effect.Post()); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Conversion - // - - /// - /// Natural transformation to an `IO` monad - /// - [Pure] - public IO, E, A> As => - effect; - - /// - /// Convert to an `IO` monad that has a runtime - /// - [Pure, MethodImpl(Opt.Default)] - public IO WithRuntime() where RT : HasIO => - IO.Lift(Transducer.compose(MinRT.convert(), Morphism)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Operators - // - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Pure ma) => - ma.ToIO(); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Fail ma) => - ma.ToIO(); - - /// - /// Convert to an `IO` monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(in Either ma) => - ma.Match(Right: Pure, Left: Fail); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer, Sum> ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer, Either> ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer> ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer> ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer, A> ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer ma) => - Lift(ma); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, IO mb) => - new(ma.effect | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, CatchError mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, CatchValue mb) => - new(Transducer.@try( - ma.Morphism, - mb.Match, - Transducer.compose(Transducer.lift(mb.Value), Transducer.mkRight()))); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, IOCatch, E, A> mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, IOCatch mb) => - new(ma.effect | mb.As); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer, A> mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer, E> mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer, Sum> mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer> mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer, Sum> mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer> mb) => - new(ma.effect | mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer, A> ma, IO mb) => - new(ma | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer, E> ma, IO mb) => - new(ma | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer ma, IO mb) => - new(ma | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer ma, IO mb) => - new(ma | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer, Sum> ma, IO mb) => - new(ma | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer> ma, IO mb) => - new(ma | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer, Sum> ma, IO mb) => - new(ma | mb.effect); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer> ma, IO mb) => - new(ma | mb.effect); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.apply.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.apply.cs deleted file mode 100644 index 12f92e00f..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.apply.cs +++ /dev/null @@ -1,216 +0,0 @@ -/* -#nullable enable -using System; -using LanguageExt.Effects.Traits; -using static LanguageExt.Prelude; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma) => - new(mf.Morphism.Apply(ma.Morphism)); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma, - IO mb) => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - IO> mf, - IO ma) => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma, - IO mb, - IO mc) => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - IO> mf, - IO ma, - IO mb) => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - IO> mf, - IO ma) => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma, - IO mb, - IO mc, - IO md) => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc).Apply(md); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - IO> mf, - IO ma, - IO mb, - IO mc) => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - IO> mf, - IO ma, - IO mb) => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>>> apply( - IO> mf, - IO ma) => - mf.Map(curry).Apply(ma); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Non-lifted functions - // - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma) => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma, - IO mb) => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - Func f, - IO ma) => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma, - IO mb, - IO mc) => - IO>.Pure(f).Apply(ma, mb, mc); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - Func f, - IO ma, - IO mb) => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - Func f, - IO ma) => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma, - IO mb, - IO mc, - IO md) => - IO>.Pure(f).Apply(ma, mb, mc, md); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - Func f, - IO ma, - IO mb, - IO mc) => - IO>.Pure(f).Apply(ma, mb, mc); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - Func f, - IO ma, - IO mb) => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>>> apply( - Func f, - IO ma) => - IO>.Pure(f).Apply(ma); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.catch.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.catch.cs deleted file mode 100644 index 544525779..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.catch.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* -using System; -using LanguageExt.ClassInstances; - -namespace LanguageExt; - -public static partial class Prelude -{ - /// - /// Catch an error if the predicate matches - /// - internal static IOCatch matchErrorIO(Func predicate, Func> Fail) => - new (predicate, Fail); - - /// - /// Catch an error if the error matches the argument provided - /// - public static IOCatch @catch(E error, Func> Fail) => - matchErrorIO(e => EqDefault.Equals(e, error), Fail); - - /// - /// Catch an error if the error matches the argument provided - /// - public static IOCatch @catch(E error, IO Fail) => - matchErrorIO(e => EqDefault.Equals(e, error), _ => Fail); - - /// - /// Catch all errors - /// - public static IOCatch @catch(IO Fail) => - matchErrorIO(static _ => true, _ => Fail); - - /// - /// Catch all errors - /// - public static IOCatch @catch(Func> Fail) => - matchErrorIO(static _ => true, Fail); - - /// - /// Catch errors - /// - public static IOCatch @catchOf(Func> Fail) - where MATCH_ERROR : E => - matchErrorIO( - static e => e is MATCH_ERROR, - e => Fail((MATCH_ERROR?)e ?? throw new InvalidCastException("casting the error can't result in null"))); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.cs deleted file mode 100644 index a5299b8be..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.cs +++ /dev/null @@ -1,444 +0,0 @@ -/* -#nullable enable -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using LanguageExt.Effects.Traits; -using System.Diagnostics.Contracts; - -namespace LanguageExt; - -public static partial class Prelude -{ - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Runtime helpers - // - - /// - /// Make the runtime into the bound value - /// - public static IO runtime() - where RT : HasIO => - IO.Lift(rt => rt); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic join - // - - /// - /// Monadic join operator - /// - /// - /// Collapses a nested IO monad so there is no nesting. - /// - /// Nest IO monad to flatten - /// Runtime - /// Error type - /// Bound value - /// Flattened IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO flatten(IO> mma) - where RT : HasIO => - new(mma.Morphism.Map(ma => ma.Map(r => r.Morphism)).Flatten()); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Zipping - // - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - (IO First, IO Second) tuple) - where RT : HasIO => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - (IO First, - IO Second, - IO Third) tuple) - where RT : HasIO => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism, tuple.Third.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - IO First, - IO Second) - where RT : HasIO => - (First, Second).Zip(); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - IO First, - IO Second, - IO Third) - where RT : HasIO => - (First, Second, Third).Zip(); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Lifting - // - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Transducer> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Transducer> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func>> f) - where RT : HasIO => - IO.LiftIO(f); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func>> f) - where RT : HasIO => - IO.LiftIO(f); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Memoisation and tail-recursion - // - - /// - /// Memoise the result, so subsequent calls don't invoke the side-IOect - /// - [Pure, MethodImpl(Opt.Default)] - public static IO memo(IO ma) - where RT : HasIO => - ma.Memo(); - - /// - /// Wrap this around the final `from` call in a `IO` LINQ expression to void a recursion induced space-leak. - /// - /// - /// - /// IO recursive(int x) => - /// from x in writeLine(x) - /// from r in tail(recursive(x + 1)) - /// select r; <--- this never runs - /// - /// - /// - /// This means the result of the LINQ expression comes from the final `from`, _not_ the `select. If the - /// type of the `final` from differs from the type of the `select` then this has no effect. - /// - /// - /// Background: When making recursive LINQ expressions, the final `select` is problematic because it means there's code - /// to run _after_ the final `from` expression. This means there's you're guaranteed to have a space-leak due to the - /// need to hold thunks to the final `select` on every recursive step. - /// - /// This function ignores the `select` altogether and says that the final `from` is where we get our return result - /// from and therefore there's no need to hold the thunk. - /// - /// IO operation - /// Runtime type - /// Error type - /// Bound value type - /// IO operation that's marked ready for tail recursion - [Pure, MethodImpl(Opt.Default)] - public static IO tail(IO ma) - where RT : HasIO => - new(Transducer.tail(ma.Morphism)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Forking - // - - /// - /// Queue this IO operation to run on the thread-pool. - /// - /// Maximum time that the forked IO operation can run for. `None` for no timeout. - /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel - /// the forked IO operation or to await the result of it. - /// - [MethodImpl(Opt.Default)] - public static IO> fork(IO ma, Option timeout = default) - where RT : HasIO => - ma.Fork(timeout); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Map and map-left - // - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO map(IO ma, Func f) - where RT : HasIO => - ma.Map(f); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO map(IO ma, Transducer f) - where RT : HasIO => - ma.Map(f); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO mapFail(IO ma, Func f) - where RT : HasIO => - ma.MapFail(f); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO mapFail(IO ma, Transducer f) - where RT : HasIO => - ma.MapFail(f); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Bi-map - // - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO bimap(IO ma, Func Succ, Func Fail) - where RT : HasIO => - ma.BiMap(Succ, Fail); - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO bimap(IO ma, Transducer Succ, Transducer Fail) - where RT : HasIO => - ma.BiMap(Succ, Fail); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Matching - // - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO match(IO ma, Func Succ, Func Fail) - where RT : HasIO => - ma.Match(Succ, Fail); - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO match(IO ma, Transducer Succ, Transducer Fail) - where RT : HasIO => - ma.Match(Succ, Fail); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO ifFail(IO ma, Func Fail) - where RT : HasIO => - ma.IfFail(Fail); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO ifFail(IO ma, Transducer Fail) - where RT : HasIO => - ma.IfFail(Fail); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO ifFail(IO ma, A Fail) - where RT : HasIO => - ma.IfFail(Fail); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public static IO ifFailIO(IO ma, Func> Fail) - where RT : HasIO => - ma.IfFailIO(Fail); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public static IO ifFailIO(IO ma, IO Fail) - where RT : HasIO => - ma.IfFailIO(Fail); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Filter - // - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - [Pure, MethodImpl(Opt.Default)] - public static IO filter(IO ma, Func predicate) - where RT : HasIO => - ma.Filter(predicate); - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - [Pure, MethodImpl(Opt.Default)] - public static IO filter(IO ma, Transducer predicate) - where RT : HasIO => - ma.Filter(predicate); - - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Synchronisation between contexts - // - - /// - /// Make a transducer run on the `SynchronizationContext` that was captured at the start - /// of an `Invoke` call. - /// - /// - /// The transducer receives its input value from the currently running sync-context and - /// then proceeds to run its operation in the captured `SynchronizationContext`: - /// typically a UI context, but could be any captured context. The result of the - /// transducer is the received back on the currently running sync-context. - /// - /// Transducer - /// - public static IO post(IO ma) - where RT : HasIO => - new(Transducer.post(ma.Morphism)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.repeat.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.repeat.cs deleted file mode 100644 index acc716db1..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.repeat.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* -using System; - -namespace LanguageExt; - -public static partial class Prelude -{ - /// - /// Keeps repeating the computation - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeat(IO ma) => - new(Transducer.repeat(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps repeating the computation, until the scheduler expires - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeat(Schedule schedule, IO ma) => - new(Transducer.repeat(schedule, ma.Morphism)); - - /// - /// Keeps repeating the computation until the predicate returns false - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatWhile( - IO ma, - Func predicate) => - new(Transducer.repeatWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatWhile( - Schedule schedule, - IO ma, - Func predicate) => - new(Transducer.repeatWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation until the predicate returns true - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatUntil( - IO ma, - Func predicate) => - new(Transducer.repeatUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatUntil( - Schedule schedule, - IO ma, - Func predicate) => - new(Transducer.repeatUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.retry.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.retry.cs deleted file mode 100644 index 66924534e..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.E.A/Prelude/IO.Prelude.retry.cs +++ /dev/null @@ -1,81 +0,0 @@ -/* -using System; - -namespace LanguageExt; - -public static partial class Prelude -{ - /// - /// Keeps retrying the computation - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retry(IO ma) => - new(Transducer.retry(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps retrying the computation, until the scheduler expires - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retry(Schedule schedule, IO ma) => - new(Transducer.retry(schedule, ma.Morphism)); - - /// - /// Keeps retrying the computation until the predicate returns false - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryWhile( - IO ma, - Func predicate) => - new(Transducer.retryWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryWhile( - Schedule schedule, - IO ma, - Func predicate) => - new(Transducer.retryWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation until the predicate returns true - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryUntil( - IO ma, - Func predicate) => - new(Transducer.retryUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryUntil( - Schedule schedule, - IO ma, - Func predicate) => - new(Transducer.retryUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Catch/IOCatch.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Catch/IOCatch.cs deleted file mode 100644 index df8084ea9..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Catch/IOCatch.cs +++ /dev/null @@ -1,59 +0,0 @@ -/* -using System; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using static LanguageExt.Prelude; -using LanguageExt.Effects.Traits; - -namespace LanguageExt -{ - public readonly struct IOCatch - where RT : HasIO - { - readonly Func> fail; - - public IOCatch(Func> fail) => - this.fail = fail; - - public IOCatch(Func predicate, Func> fail) : - this(e => predicate(e) ? fail(e) : Fail(e)) - { } - - [Pure, MethodImpl(Opt.Default)] - public IO Run(E error) => - fail(error); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(CatchValue ma, IOCatch mb) => - new (e => ma.Match(e) ? Pure(ma.Value(e)) : mb.Run(e)); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(CatchError ma, IOCatch mb) => - new (e => ma.Match(e) ? Fail(ma.Value(e)) : mb.Run(e)); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(IOCatch ma, CatchValue mb) => - new (e => - ma.Run(e) - .Match>( - Succ: x => Pure(x), - Fail: e1 => mb.Match(e1) - ? Pure(mb.Value(e1)) - : Fail(e1)).Flatten()); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(IOCatch ma, CatchError mb) => - new (e => - ma.Run(e) - .Match>( - Succ: x => Pure(x), - Fail: e1 => mb.Match(e1) - ? Fail(mb.Value(e1)) - : Fail(e1)).Flatten()); - - [Pure, MethodImpl(Opt.Default)] - public static IOCatch operator |(IOCatch ma, IOCatch mb) => - new (e => ma.Run(e) | mb.Run(e)); - } -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Apply.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Apply.cs deleted file mode 100644 index b37a2aec0..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Apply.cs +++ /dev/null @@ -1,129 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; -using static LanguageExt.Prelude; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma) - where RT : HasIO => - new(mf.Morphism.Apply(ma.Morphism)); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma, - IO mb) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this IO> mf, - IO ma) - where RT : HasIO => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma, - IO mb, - IO mc) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this IO> mf, - IO ma, - IO mb) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> Apply( - this IO> mf, - IO ma) - where RT : HasIO => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO Apply( - this IO> mf, - IO ma, - IO mb, - IO mc, - IO md) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc).Apply(md); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> Apply( - this IO> mf, - IO ma, - IO mb, - IO mc) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> Apply( - this IO> mf, - IO ma, - IO mb) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>>> Apply( - this IO> mf, - IO ma) - where RT : HasIO => - mf.Map(curry).Apply(ma); - - /// - /// Applicative action: runs the first applicative, ignores the result, and returns the second applicative - /// - public static IO Action( - this IO ma, - IO mb) - where RT : HasIO => - new(ma.Morphism.Action(mb.Morphism)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Catch.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Catch.cs deleted file mode 100644 index caca524a5..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Catch.cs +++ /dev/null @@ -1,48 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; -using static LanguageExt.Prelude; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Catch an error if the error matches the argument provided - /// - public static IO Catch(this IO ma, E Error, Func> Fail) - where RT : HasIO => - ma | @catch(Error, Fail); - - /// - /// Catch an error if the error matches the argument provided - /// - public static IO Catch(this IO ma, E Error, IO Fail) - where RT : HasIO => - ma | @catch(Error, Fail); - - /// - /// Catch all errors - /// - public static IO Catch(this IO ma, IO Fail) - where RT : HasIO => - ma | @catch(Fail); - - /// - /// Catch all errors - /// - public static IO Catch(this IO ma, Func> Fail) - where RT : HasIO => - ma | @catch(Fail); - - /// - /// Catch errors - /// - public static IO CatchOf( - this IO ma, - Func> Fail) - where RT : HasIO - where MATCH_ERROR : E => - ma | @catchOf(Fail); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Repeat.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Repeat.cs deleted file mode 100644 index fabb8876c..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Repeat.cs +++ /dev/null @@ -1,86 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Keeps repeating the computation - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO Repeat(this IO ma) - where RT : HasIO => - new(Transducer.repeat(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps repeating the computation, until the scheduler expires - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO Repeat(this IO ma, Schedule schedule) - where RT : HasIO => - new(Transducer.repeat(schedule, ma.Morphism)); - - /// - /// Keeps repeating the computation until the predicate returns false - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatWhile(this IO ma, Func predicate) - where RT : HasIO => - new(Transducer.repeatWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatWhile( - this IO ma, - Schedule schedule, - Func predicate) - where RT : HasIO => - new(Transducer.repeatWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation until the predicate returns true - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatUntil( - this IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.repeatUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO RepeatUntil( - this IO ma, - Schedule schedule, - Func predicate) - where RT : HasIO => - new(Transducer.repeatUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Retry.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Retry.cs deleted file mode 100644 index 7364e2da3..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.Retry.cs +++ /dev/null @@ -1,91 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - /// - /// Keeps retrying the computation - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO Retry( - this IO ma) - where RT : HasIO => - new(Transducer.retry(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps retrying the computation, until the scheduler expires - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO Retry( - this IO ma, - Schedule schedule) - where RT : HasIO => - new(Transducer.retry(schedule, ma.Morphism)); - - /// - /// Keeps retrying the computation until the predicate returns false - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryWhile( - this IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.retryWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryWhile( - this IO ma, - Schedule schedule, - Func predicate) - where RT : HasIO => - new(Transducer.retryWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation until the predicate returns true - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryUntil( - this IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.retryUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO RetryUntil( - this IO ma, - Schedule schedule, - Func predicate) - where RT : HasIO => - new(Transducer.retryUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.cs deleted file mode 100644 index bef17a38c..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Extensions/IO.Extensions.cs +++ /dev/null @@ -1,252 +0,0 @@ -/* -using System; -using LanguageExt.Common; -using LanguageExt.Effects.Traits; -using LanguageExt.Traits; -using static LanguageExt.Prelude; - -namespace LanguageExt; - -public static partial class IOExtensions -{ - public static IO As(this K.Runtime, A> ma) - where RT : HasIO => - (IO)ma; - - /// - /// Lift transducer into an effect monad - /// - public static IO ToIO(this Transducer> t) - where RT : HasIO => - new (t); - - /// - /// Lift transducer in an effect monad - /// - public static IO ToIO(this Transducer t) - where RT : HasIO => - new(t); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic join - // - - /// - /// Monadic join operator - /// - /// - /// Collapses a nested IO monad so there is no nesting. - /// - /// Nest IO monad to flatten - /// Runtime - /// Error type - /// Bound value - /// Flattened IO monad - public static IO Flatten(this IO> mma) - where RT : HasIO => - mma.Bind(ma => ma); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Binding extensions - // - - public static IO Bind(this Transducer ma, Func> f) - where RT : HasIO => - IO.Lift(Transducer.compose(Transducer.constant(default), ma.Morphism)).Bind(f); - - public static IO Bind(this Transducer> ma, Func> f) - where RT : HasIO => - IO.Lift(Transducer.compose(Transducer.constant(default), ma.Morphism)).Bind(f); - - public static IO Bind(this Transducer ma, Func> f) - where RT : HasIO => - IO.Lift(ma.Morphism).Bind(f); - - public static IO Bind(this Transducer> ma, Func> f) - where RT : HasIO => - IO.Lift(ma.Morphism).Bind(f); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // SelectMany extensions - // - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this (IO First, IO Second) self, - Func<(A First, B Second), IO> bind, - Func<(A First, B Second), C, D> project) - where RT : HasIO => - self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))); - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this IO self, - Func First, IO Second)> bind, - Func project) - where RT : HasIO => - self.Bind(a => bind(a).Zip().Map(cd => project(a, cd))); - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this (IO First, IO Second, IO Third) self, - Func<(A First, B Second, C Third), IO> bind, - Func<(A First, B Second, C Third), D, E> project) - where RT : HasIO => - self.Zip().Bind(ab => bind(ab).Map(c => project(ab, c))); - - /// - /// Monadic bind and project with paired IO monads - /// - public static IO SelectMany( - this IO self, - Func First, IO Second, IO Third)> bind, - Func project) - where RT : HasIO => - self.Bind(a => bind(a).Zip().Map(cd => project(a, cd))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer ma, - Func> bind, - Func project) - where RT : HasIO => - IO.Lift(Transducer.compose(Transducer.constant(default), ma)) - .Bind(x => bind(x).Map(y => project(x, y))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer ma, - Func> bind, - Func project) - where RT : HasIO => - IO.Lift(ma).Bind(x => bind(x).Map(y => project(x, y))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer> ma, - Func> bind, - Func project) - where RT : HasIO => - IO.Lift(Transducer.compose(Transducer.constant(default), ma)) - .Bind(x => bind(x).Map(y => project(x, y))); - - /// - /// Monadic bind and project - /// - public static IO SelectMany( - this Transducer> ma, - Func> bind, - Func project) - where RT : HasIO => - IO.Lift(ma).Bind(x => bind(x).Map(y => project(x, y))); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Zipping - // - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - public static IO Zip( - this (IO First, IO Second) tuple) - where RT : HasIO => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - public static IO Zip( - this (IO First, - IO Second, - IO Third) tuple) - where RT : HasIO => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism, tuple.Third.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - public static IO Zip( - this IO First, - IO Second) - where RT : HasIO => - (First, Second).Zip(); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - public static IO Zip( - this IO First, - IO Second, - IO Third) - where RT : HasIO => - (First, Second, Third).Zip(); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Guards - // - - /// - /// Natural transformation to `Eff` - /// - public static Eff ToEff(this Guard guard) - where RT : HasIO => - guard.Flag - ? Pure(unit) - : Fail(guard.OnFalse()); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/ForkIO.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/ForkIO.cs deleted file mode 100644 index 8802ea764..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/ForkIO.cs +++ /dev/null @@ -1,29 +0,0 @@ -/* -#nullable enable -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -/// -/// Result of forking an IO monad -/// -/// An IO monad, which if invoked, would cancel the forked IO operation -/// An IO monad, which if invoked, would attempt to get the result of the forked IO operation -/// Bound value type -public readonly record struct ForkIO( - IO Cancel, - IO Await) - where RT : HasIO; - - -/// -/// Result of forking an IO monad -/// -/// An IO monad, which if invoked, would cancel the forked IO operation -/// An IO monad, which if invoked, would attempt to get the result of the forked IO operation -/// Bound value type -public readonly record struct ForkIO( - IO Cancel, - IO Await); - */ - diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/IO.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/IO.cs deleted file mode 100644 index cbc688b75..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/IO.cs +++ /dev/null @@ -1,1336 +0,0 @@ -/* -using System; -using LanguageExt.Common; -using static LanguageExt.Prelude; -using System.Diagnostics.Contracts; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using LanguageExt.Effects.Traits; -using LanguageExt.Traits; - -namespace LanguageExt; - -/// -/// Transducer based IO monad -/// -/// Runtime struct -/// Error value type -/// Bound value type -public readonly struct IO : K.Runtime, A> - where RT : HasIO -{ - /// - /// Underlying transducer that captures all of the IO behaviour - /// - readonly Transducer> thunk; - - public K.Runtime, A> this; - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Constructors - // - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - internal IO(Transducer> thunk) => - this.thunk = thunk; - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func> thunk) => - this.thunk = lift(thunk); - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func thunk) - : this(rt => Sum.Right(thunk(rt))) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - internal IO(Transducer thunk) - : this(Transducer.compose(thunk, Transducer.mkRight())) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Func> thunk) - : this(rt => thunk(rt).ToSum()) - { } - - /// - /// Constructor - /// - [MethodImpl(Opt.Default)] - IO(Transducer> thunk) - : this(Transducer.compose(thunk, lift, Sum>(x => x.ToSum()))) - { } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Transducer - // - - /// - /// Access to the underlying transducer - /// - [Pure] - public Transducer> Morphism => - thunk ?? Transducer.fail>(Errors.Bottom); - - /// - /// Reduction of the underlying transducer - /// - /// Reducer - /// - /// - [Pure, MethodImpl(Opt.Default)] - public Reducer Transform(Reducer, S> reduce) => - Morphism.Transform(reduce); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Invoking - // - - /// - /// All IO monads will catch exceptions at their point of invocation (i.e. in the `Run*` methods). - /// But they do not catch exceptions elsewhere without explicitly using this `Try()` method. - /// - /// This wraps the `IO` monad in a try/catch that converts exceptional errors to an `E` and - /// therefore puts the `IO` in a `Fail` state the expression that can then be matched upon. - /// - /// - /// Useful when you want exceptions to be dealt with through matching/bi-mapping/etc. - /// and not merely be caught be the exception handler in `Run*`. - /// - /// - /// This is used automatically for the first argument in the coalescing `|` operator so that any - /// exceptional failures, in the first argument, allow the second argument to be invoked. - /// - [Pure, MethodImpl(Opt.Default)] - public IO Try() => - new(Transducer.@try(Morphism, _ => true, Transducer.mkLeft())); - - /// - /// Invoke the effect - /// - [Pure, MethodImpl(Opt.Default)] - public Either Run(RT env) => - Morphism.Run1(env, env.CancellationToken, env.SynchronizationContext) - .ToEither(RT.FromError); - - /// - /// Invoke the effect (which could produce multiple values). Run a reducer for - /// each value yielded. - /// - [Pure, MethodImpl(Opt.Default)] - public Fin RunMany(RT env, S initialState, Func, TResult> reducer) => - Morphism.Run( - env, - initialState, - Reducer.from, S>( - (_, s, sv) => sv switch - { - SumRight r => reducer(s, Either.Right(r.Value)), - SumLeft l => reducer(s, Either.Left(l.Value)), - _ => TResult.Complete(s) - }), - env.CancellationToken, - env.SynchronizationContext) - .ToFin(); - - /// - /// Invoke the effect (which could produce multiple values). Collect those results in a `Seq` - /// - [Pure, MethodImpl(Opt.Default)] - public Either> RunMany(RT env) => - RunMany(env, - Either>.Right(Seq()), - (s, v) => - (s.IsRight, v.IsRight) switch - { - (true, true) => TResult.Continue(Either>.Right(((Seq)s).Add((A)v))), - (true, false) => TResult.Complete(Either>.Left((E)v)), - _ => TResult.Complete(s) - }) - .Match( - Succ: v => v, - Fail: RT.FromError); - - /// - /// Invoke the effect asynchronously - /// - [Pure, MethodImpl(Opt.Default)] - public Task> RunAsync(RT env) => - Morphism.Run1Async(env, null, env.CancellationToken, env.SynchronizationContext) - .Map(r => r.ToEither(RT.FromError)); - - /// - /// Invoke the effect (which could produce multiple values). Run a reducer for - /// each value yielded. - /// - [Pure, MethodImpl(Opt.Default)] - public Task> RunManyAsync(RT env, S initialState, Func, TResult> reducer) => - Morphism.RunAsync( - env, - initialState, - Reducer.from, S>( - (_, s, sv) => sv switch - { - SumRight r => reducer(s, Either.Right(r.Value)), - SumLeft l => reducer(s, Either.Left(l.Value)), - _ => TResult.Complete(s) - }), - null, - env.CancellationToken, - env.SynchronizationContext) - .Map(r => r.ToFin()); - - /// - /// Invoke the effect (which could produce multiple values). Collect those results in a `Seq` - /// - [Pure, MethodImpl(Opt.Default)] - public Task>> RunManyAsync(RT env) => - RunManyAsync(env, - Either>.Right(Seq()), - (s, v) => - (s.IsRight, v.IsRight) switch - { - (true, true) => TResult.Continue(Either>.Right(((Seq)s).Add((A)v))), - (true, false) => TResult.Complete(Either>.Left((E)v)), - _ => TResult.Complete(s) - }) - .Map(r => r.Match( - Succ: v => v, - Fail: RT.FromError)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Timeout - // - - /// - /// Cancel the operation if it takes too long - /// - /// Timeout period - /// An IO operation that will timeout if it takes too long - [Pure, MethodImpl(Opt.Default)] - public IO Timeout(TimeSpan timeoutDelay) - { - var self = this; - return LiftIO(async rt => - { - using var delayTokSrc = new CancellationTokenSource(); - var lenv = rt.LocalCancel; - var delay = Task.Delay(timeoutDelay, delayTokSrc.Token); - var task = self.RunAsync(lenv); - var completed = await Task.WhenAny(delay, task).ConfigureAwait(false); - - if (completed == delay) - { - try - { - await lenv.CancellationTokenSource.CancelAsync().ConfigureAwait(false); - } - catch - { - // The token-source might have already been disposed, so let's ignore that error - } - return Left(RT.FromError(Errors.TimedOut)); - } - else - { - await delayTokSrc.CancelAsync().ConfigureAwait(false); - return await task.ConfigureAwait(false); - } - }); - } - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Lifting - // - - /// - /// Lift a value into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Pure(A value) => - new (_ => Sum.Right(value)); - - /// - /// Lift a failure into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Fail(E error) => - new (_ => Sum.Left(error)); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer> f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Func f) => - new (f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO Lift(Transducer f) => - new (f); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO LiftIO(Func> f) => - new (liftAsync>( - async (_, rt) => Sum.Right(await f(rt).ConfigureAwait(false)))); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO LiftIO(Func>> f) => - new(liftAsync>( - async (_, rt) => await f(rt).ConfigureAwait(false))); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO LiftIO(Func>> f) => - new (liftAsync>( - async (_, rt) => (await f(rt).ConfigureAwait(false)).ToSum())); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Memoisation and tail calls - // - - /// - /// Memoise the result, so subsequent calls don't invoke the side-effect - /// - [Pure, MethodImpl(Opt.Default)] - public IO Memo() => - new(Transducer.memo(Morphism)); - - /// - /// Wrap this around the final `from` call in a `IO` LINQ expression to void a recursion induced space-leak. - /// - /// - /// - /// IO recursive(int x) => - /// from x in writeLine(x) - /// from r in tail(recursive(x + 1)) - /// select r; <--- this never runs - /// - /// - /// - /// This means the result of the LINQ expression comes from the final `from`, _not_ the `select. If the - /// type of the `final` from differs from the type of the `select` then this has no effect. - /// - /// - /// Background: When making recursive LINQ expressions, the final `select` is problematic because it means - /// there's code to run _after_ the final `from` expression. This means there's you're guaranteed to have a - /// space-leak due to the need to hold thunks to the final `select` on every recursive step. - /// - /// This function ignores the `select` altogether and says that the final `from` is where we get our return - /// result from and therefore there's no need to hold the thunk. - /// - /// IO operation that's marked ready for tail recursion - [Pure, MethodImpl(Opt.Default)] - public IO Tail() => - new(tail(Morphism)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Forking - // - - /// - /// Queue this IO operation to run on the thread-pool. - /// - /// Maximum time that the forked IO operation can run for. `None` for no timeout. - /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel - /// the forked IO operation or to await the result of it. - /// - [MethodImpl(Opt.Default)] - public IO> Fork(Option timeout = default) => - new(Transducer.fork(Morphism, timeout).Map(TFork.ToIO)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Map and map-left - // - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO Map(Func f) => - new(Transducer.mapRight(Morphism, f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO Select(Func f) => - new(Transducer.mapRight(Morphism, f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO Map(Transducer f) => - new(Transducer.mapRight(Morphism, f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO MapFail(Func f) => - new(Transducer.mapLeft(Morphism, f)); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO MapFail(Transducer f) => - new(Transducer.mapLeft(Morphism, f)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Bi-map - // - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO BiMap(Func Succ, Func Fail) => - new(Transducer.bimap(Morphism, Fail, Succ)); - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public IO BiMap(Transducer Succ, Transducer Fail) => - new(Transducer.bimap(Morphism, Fail, Succ)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Matching - // - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure] - public IO Match(Func Succ, Func Fail) => - Match(lift(Succ), lift(Fail)); - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure] - public IO Match(Transducer Succ, Transducer Fail) => - new(Transducer.compose( - Transducer.bimap(Morphism, Fail, Succ), - lift, Sum>(s => s switch - { - SumRight r => Sum.Right(r.Value), - SumLeft l => Sum.Right(l.Value), - _ => throw new BottomException() - }))); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public IO IfFail(Func Fail) => - IfFail(lift(Fail)); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public IO IfFail(Transducer Fail) => - Match(Transducer.identity(), Fail); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public IO IfFail(A Fail) => - Match(Transducer.identity(), Transducer.constant(Fail)); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public IO IfFailIO(Func> Fail) => - new(Transducer.bimap( - Morphism, - e => Fail(e).Morphism, - x => Transducer.constant>(Sum.Right(x))) - .Flatten()); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public IO IfFailIO(IO Fail) => - IfFailIO(_ => Fail); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Filter - // - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - public IO Filter(Func predicate) => - Filter(lift(predicate)); - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - public IO Filter(Transducer predicate) => - new(Transducer.filter(Morphism, predicate)); - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - public IO Where(Func predicate) => - Filter(lift(predicate)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic binding - // - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO Bind(Func> f) => - new(Transducer.bind(Morphism, x => f(x).Morphism)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO Bind(Func.Runtime, B>> f) => - new(Transducer.bind(Morphism, x => f(x).As().Morphism)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// transducer provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the transducer provided - public IO Bind(Transducer> f) => - Map(f).Flatten(); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO Bind(Func> f) => - Bind(x => f(x).ToIO()); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO Bind(Func> f) => - Bind(x => f(x).ToIO()); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO Bind(Func> f) => - new(Transducer.mapRight(Morphism, lift(f).Flatten())); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO Bind(Func> f) => - Bind(x => IO.Lift(f(x))); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic binding and projection - // - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO SelectMany(Func> bind, Func project) => - new(Transducer.selectMany(Morphism, x => bind(x).Morphism, project)); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO SelectMany(Func> bind, Func project) => - SelectMany(x => bind(x).ToIO(), project); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO SelectMany(Func> bind, Func project) => - SelectMany(x => bind(x).ToIO(), project); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO SelectMany(Func> bind, Func project) => - Bind(x => bind(x) switch - { - { Flag: true } => IO.Pure(project(x, default)), - var g => IO.Fail(g.OnFalse()) - }); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO SelectMany(Func, Unit>> bind, Func project) => - Bind(x => bind(x) switch - { - { Flag: true } => IO.Pure(project(x, default)), - var g => IO.Fail(g.OnFalse().Value) - }); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO SelectMany(Func> bind, Func project) => - Bind(x => bind(x).Map(y => project(x, y))); - - /// - /// Monadic bind operation. This runs the current IO monad and feeds its result to the - /// function provided; which in turn returns a new IO monad. This can be thought of as - /// chaining IO operations sequentially. - /// - /// Bind operation - /// Composition of this monad and the result of the function provided - public IO SelectMany(Func> bind, Func project) => - Bind(x => bind(x).Map(y => project(x, y))); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Folding - // - - /// - /// Fold the effect forever or until the schedule expires - /// - [Pure, MethodImpl(Opt.Default)] - public IO Fold( - Schedule schedule, - S initialState, - Func folder) => - new(Transducer.compose( - Morphism, - Transducer.foldSum( - schedule, - initialState, - folder, - _ => TResult.Continue(unit)))); - - /// - /// Fold the effect forever - /// - [Pure, MethodImpl(Opt.Default)] - public IO Fold( - S initialState, - Func folder) => - Fold(Schedule.Forever, initialState, folder); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - Schedule schedule, - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - new(Transducer.compose( - Morphism, - Transducer.foldSum( - schedule, - initialState, - folder, - last => predicate(last) ? TResult.Complete(unit) : TResult.Continue(unit)))); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - Schedule schedule, - S initialState, - Func folder, - Func stateIs) => - FoldUntil(schedule, initialState, folder, last => stateIs(last.State)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - Schedule schedule, - S initialState, - Func folder, - Func valueIs) => - FoldUntil(schedule, initialState, folder, last => valueIs(last.Value)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - new(Transducer.compose( - Morphism, - Transducer.foldSum( - Schedule.Forever, - initialState, - folder, - last => predicate(last) ? TResult.Continue(unit) : TResult.Complete(unit)))); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - S initialState, - Func folder, - Func stateIs) => - FoldUntil(Schedule.Forever, initialState, folder, last => stateIs(last.State)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntil( - S initialState, - Func folder, - Func valueIs) => - FoldUntil(Schedule.Forever, initialState, folder, last => valueIs(last.Value)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - Schedule schedule, - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - new(Transducer.compose( - Morphism, - Transducer.foldSum( - schedule, - initialState, - folder, - last => predicate(last) ? TResult.Complete(unit) : TResult.Continue(unit)))); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - Schedule schedule, - S initialState, - Func folder, - Func stateIs) => - FoldWhile(schedule, initialState, folder, last => stateIs(last.State)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - Schedule schedule, - S initialState, - Func folder, - Func valueIs) => - FoldWhile(schedule, initialState, folder, last => valueIs(last.Value)); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - S initialState, - Func folder, - Func<(S State, A Value), bool> predicate) => - FoldWhile(Schedule.Forever, initialState, folder, predicate); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - S initialState, - Func folder, - Func stateIs) => - FoldWhile(Schedule.Forever, initialState, folder, last => stateIs(last.State)); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhile( - S initialState, - Func folder, - Func valueIs) => - FoldWhile(Schedule.Forever, initialState, folder, last => valueIs(last.Value)); - - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Folding, where each fold is monadic - // - - /// - /// Fold the effect forever or until the schedule expires - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldM( - Schedule schedule, - S initialState, - Func> folder) => - Fold(schedule, IO.Pure(initialState), (ms, a) => ms.Bind(s => folder(s, a))) - .Flatten(); - - /// - /// Fold the effect forever - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldM( - S initialState, - Func> folder) => - FoldM(Schedule.Forever, initialState, folder); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntilM( - Schedule schedule, - S initialState, - Func> folder, - Func valueIs) => - FoldUntil(schedule, IO.Pure(initialState), (ms, a) => ms.Bind(s => folder(s, a)), valueIs) - .Flatten(); - - /// - /// Fold the effect until the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldUntilM( - S initialState, - Func> folder, - Func valueIs) => - FoldUntilM(Schedule.Forever, initialState, folder, valueIs); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhileM( - Schedule schedule, - S initialState, - Func> folder, - Func valueIs) => - FoldWhile(schedule, IO.Pure(initialState), (ms, a) => ms.Bind(s => folder(s, a)), valueIs) - .Flatten(); - - /// - /// Fold the effect while the predicate returns `true` - /// - [Pure, MethodImpl(Opt.Default)] - public IO FoldWhileM( - S initialState, - Func> folder, - Func valueIs) => - FoldWhileM(Schedule.Forever, initialState, folder, valueIs); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Synchronisation between contexts - // - - /// - /// Make a transducer run on the `SynchronizationContext` that was captured at the start - /// of an `Invoke` call. - /// - /// - /// The transducer receives its input value from the currently running sync-context and - /// then proceeds to run its operation in the captured `SynchronizationContext`: - /// typically a UI context, but could be any captured context. The result of the - /// transducer is the received back on the currently running sync-context. - /// - /// Transducer - /// - [Pure, MethodImpl(Opt.Default)] - public IO Post() => - new(Transducer.post(Morphism)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Operators - // - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Pure ma) => - ma.ToIO(); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Fail ma) => - ma.ToIO(); - - /// - /// Convert to an `IO` monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(in Either ma) => - ma.Match(Right: Pure, Left: Fail); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer> ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer> ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer> ma) => - Lift(Transducer.compose(Transducer.constant(default), ma)); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer> ma) => - Lift(Transducer.compose(Transducer.constant(default), ma)); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer ma) => - Lift(ma); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(Transducer ma) => - Lift(Transducer.compose(Transducer.constant(default), ma)); - - /// - /// Convert to an IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static implicit operator IO(IO ma) => - ma.WithRuntime(); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, IO mb) => - new(Transducer.choice(ma.Try().Morphism, mb.Morphism)); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, CatchError mb) => - new(Transducer.@try( - ma.Morphism, - mb.Match, - Transducer.compose(lift(mb.Value), Transducer.mkLeft()))); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, CatchValue mb) => - new(Transducer.@try( - ma.Morphism, - mb.Match, - Transducer.compose(lift(mb.Value), Transducer.mkRight()))); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, IOCatch mb) => - ma.Try().Match(Succ: Pure, Fail: mb.Run).Flatten(); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer mb) => - ma.Try() | new IO(Transducer.compose(mb, Transducer.mkRight())); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer mb) => - ma.Try() | new IO(Transducer.compose(mb, Transducer.mkLeft())); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer mb) => - ma.Try() | new IO( - Transducer.compose( - Transducer.constant(default), - mb, - Transducer.mkRight())); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer mb) => - ma.Try() | new IO( - Transducer.compose( - Transducer.constant(default), - mb, - Transducer.mkLeft())); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer> mb) => - ma.Try() | new IO(mb); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer> mb) => - ma.Try() | new IO(Transducer.compose(Transducer.constant(default), mb)); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer> mb) => - ma.Try() | new IO(Transducer.mapLeft(mb, lift(e => RT.FromError(e)))); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(IO ma, Transducer> mb) => - ma.Try() | new IO( - Transducer.compose( - Transducer.constant(default), - Transducer.mapLeft(mb, lift(e => RT.FromError(e))))); - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer ma, IO mb) => - new IO(Transducer.compose(ma, Transducer.mkRight())) | mb; - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer ma, IO mb) => - new IO(Transducer.compose(ma, Transducer.mkLeft())).Try() | mb; - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer ma, IO mb) => - new IO( - Transducer.compose( - Transducer.constant(default), - ma, - Transducer.mkRight())).Try() | mb; - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer ma, IO mb) => - new IO( - Transducer.compose( - Transducer.constant(default), - ma, - Transducer.mkLeft())).Try() | mb; - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer> ma, IO mb) => - new IO(ma).Try() | mb; - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer> ma, IO mb) => - new IO(Transducer.compose(Transducer.constant(default), ma)).Try() | mb; - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer> ma, IO mb) => - new IO(Transducer.mapLeft(ma, lift(e => RT.FromError(e)))).Try() | mb; - - /// - /// Run the first IO operation; if it fails, run the second. Otherwise return the - /// result of the first without running the second. - /// - /// First IO operation - /// Alternative IO operation - /// Result of either the first or second operation - [Pure, MethodImpl(Opt.Default)] - public static IO operator |(Transducer> ma, IO mb) => - new IO( - Transducer.compose( - Transducer.constant(default), - Transducer.mapLeft(ma, lift(e => RT.FromError(e))))).Try() | mb; -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.apply.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.apply.cs deleted file mode 100644 index 014f26d4f..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.apply.cs +++ /dev/null @@ -1,234 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -public static partial class Prelude -{ - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma) - where RT : HasIO => - new(mf.Morphism.Apply(ma.Morphism)); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma, - IO mb) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - IO> mf, - IO ma) - where RT : HasIO => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma, - IO mb, - IO mc) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - IO> mf, - IO ma, - IO mb) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - IO> mf, - IO ma) - where RT : HasIO => - mf.Map(curry).Apply(ma); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - IO> mf, - IO ma, - IO mb, - IO mc, - IO md) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc).Apply(md); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - IO> mf, - IO ma, - IO mb, - IO mc) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb).Apply(mc); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - IO> mf, - IO ma, - IO mb) - where RT : HasIO => - mf.Map(curry).Apply(ma).Apply(mb); - - /// - /// Applicative apply: takes the lifted function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>>> apply( - IO> mf, - IO ma) - where RT : HasIO => - mf.Map(curry).Apply(ma); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Non-lifted functions - // - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma) - where RT : HasIO => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma, - IO mb) - where RT : HasIO => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - Func f, - IO ma) - where RT : HasIO => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma, - IO mb, - IO mc) - where RT : HasIO => - IO>.Pure(f).Apply(ma, mb, mc); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - Func f, - IO ma, - IO mb) - where RT : HasIO => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - Func f, - IO ma) - where RT : HasIO => - IO>.Pure(f).Apply(ma); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO apply( - Func f, - IO ma, - IO mb, - IO mc, - IO md) - where RT : HasIO => - IO>.Pure(f).Apply(ma, mb, mc, md); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO> apply( - Func f, - IO ma, - IO mb, - IO mc) - where RT : HasIO => - IO>.Pure(f).Apply(ma, mb, mc); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>> apply( - Func f, - IO ma, - IO mb) - where RT : HasIO => - IO>.Pure(f).Apply(ma, mb); - - /// - /// Applicative apply: takes the function and the lifted argument, applies the function to the argument - /// and returns the result, lifted. - /// - public static IO>>> apply( - Func f, - IO ma) - where RT : HasIO => - IO>.Pure(f).Apply(ma); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.catch.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.catch.cs deleted file mode 100644 index ddea4940b..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.catch.cs +++ /dev/null @@ -1,55 +0,0 @@ -/* -using System; -using LanguageExt.ClassInstances; -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -public static partial class Prelude -{ - /// - /// Catch an error if the predicate matches - /// - internal static IOCatch matchErrorIO(Func predicate, Func> Fail) - where RT : HasIO => - new (predicate, Fail); - - /// - /// Catch an error if the error matches the argument provided - /// - public static IOCatch @catch(E error, Func> Fail) - where RT : HasIO => - matchErrorIO(e => EqDefault.Equals(e, error), Fail); - - /// - /// Catch an error if the error matches the argument provided - /// - public static IOCatch @catch(E error, IO Fail) - where RT : HasIO => - matchErrorIO(e => EqDefault.Equals(e, error), _ => Fail); - - /// - /// Catch all errors - /// - public static IOCatch @catch(IO Fail) - where RT : HasIO => - matchErrorIO(static _ => true, _ => Fail); - - /// - /// Catch all errors - /// - public static IOCatch @catch(Func> Fail) - where RT : HasIO => - matchErrorIO(static _ => true, Fail); - - /// - /// Catch errors - /// - public static IOCatch @catchOf(Func> Fail) - where RT : HasIO - where MATCH_ERROR : E => - matchErrorIO( - static e => e is MATCH_ERROR, - e => Fail((MATCH_ERROR?)e ?? throw new InvalidCastException("casting the error can't result in null"))); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.cs deleted file mode 100644 index 6369bee25..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.cs +++ /dev/null @@ -1,443 +0,0 @@ -/* -using System; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using LanguageExt.Effects.Traits; -using System.Diagnostics.Contracts; - -namespace LanguageExt; - -public static partial class Prelude -{ - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Runtime helpers - // - - /// - /// Make the runtime into the bound value - /// - public static IO runtime() - where RT : HasIO => - IO.Lift(rt => rt); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Monadic join - // - - /// - /// Monadic join operator - /// - /// - /// Collapses a nested IO monad so there is no nesting. - /// - /// Nest IO monad to flatten - /// Runtime - /// Error type - /// Bound value - /// Flattened IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO flatten(IO> mma) - where RT : HasIO => - new(mma.Morphism.Map(ma => ma.Map(r => r.Morphism)).Flatten()); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Zipping - // - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - (IO First, IO Second) tuple) - where RT : HasIO => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - (IO First, - IO Second, - IO Third) tuple) - where RT : HasIO => - new(Transducer.zip(tuple.First.Morphism, tuple.Second.Morphism, tuple.Third.Morphism)); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - IO First, - IO Second) - where RT : HasIO => - (First, Second).Zip(); - - /// - /// Takes two IO monads and zips their result - /// - /// - /// Asynchronous operations will run concurrently - /// - /// Tuple of IO monads to run - /// Runtime type - /// Error type - /// First IO monad bound value type - /// Second IO monad bound value type - /// Third IO monad bound value type - /// IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO zip( - IO First, - IO Second, - IO Third) - where RT : HasIO => - (First, Second, Third).Zip(); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Lifting - // - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Transducer> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a synchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Transducer> f) - where RT : HasIO => - IO.Lift(f); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func>> f) - where RT : HasIO => - IO.LiftIO(f); - - /// - /// Lift a asynchronous effect into the IO monad - /// - [Pure, MethodImpl(Opt.Default)] - public static IO liftIO(Func>> f) - where RT : HasIO => - IO.LiftIO(f); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Memoisation and tail-recursion - // - - /// - /// Memoise the result, so subsequent calls don't invoke the side-IOect - /// - [Pure, MethodImpl(Opt.Default)] - public static IO memo(IO ma) - where RT : HasIO => - ma.Memo(); - - /// - /// Wrap this around the final `from` call in a `IO` LINQ expression to void a recursion induced space-leak. - /// - /// - /// - /// IO recursive(int x) => - /// from x in writeLine(x) - /// from r in tail(recursive(x + 1)) - /// select r; <--- this never runs - /// - /// - /// - /// This means the result of the LINQ expression comes from the final `from`, _not_ the `select. If the - /// type of the `final` from differs from the type of the `select` then this has no effect. - /// - /// - /// Background: When making recursive LINQ expressions, the final `select` is problematic because it means there's code - /// to run _after_ the final `from` expression. This means there's you're guaranteed to have a space-leak due to the - /// need to hold thunks to the final `select` on every recursive step. - /// - /// This function ignores the `select` altogether and says that the final `from` is where we get our return result - /// from and therefore there's no need to hold the thunk. - /// - /// IO operation - /// Runtime type - /// Error type - /// Bound value type - /// IO operation that's marked ready for tail recursion - [Pure, MethodImpl(Opt.Default)] - public static IO tail(IO ma) - where RT : HasIO => - new(tail(ma.Morphism)); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Forking - // - - /// - /// Queue this IO operation to run on the thread-pool. - /// - /// Maximum time that the forked IO operation can run for. `None` for no timeout. - /// Returns a `ForkIO` data-structure that contains two IO effects that can be used to either cancel - /// the forked IO operation or to await the result of it. - /// - [MethodImpl(Opt.Default)] - public static IO> fork(IO ma, Option timeout = default) - where RT : HasIO => - ma.Fork(timeout); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Map and map-left - // - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO map(IO ma, Func f) - where RT : HasIO => - ma.Map(f); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO map(IO ma, Transducer f) - where RT : HasIO => - ma.Map(f); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO mapFail(IO ma, Func f) - where RT : HasIO => - ma.MapFail(f); - - /// - /// Maps the IO monad if it's in a success state - /// - /// Function to map the success value with - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO mapFail(IO ma, Transducer f) - where RT : HasIO => - ma.MapFail(f); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Bi-map - // - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO bimap(IO ma, Func Succ, Func Fail) - where RT : HasIO => - ma.BiMap(Succ, Fail); - - /// - /// Mapping of either the Success state or the Failure state depending on what - /// state this IO monad is in. - /// - /// Mapping to use if the IO monad is in a success state - /// Mapping to use if the IO monad is in a failure state - /// Mapped IO monad - [Pure, MethodImpl(Opt.Default)] - public static IO bimap(IO ma, Transducer Succ, Transducer Fail) - where RT : HasIO => - ma.BiMap(Succ, Fail); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Matching - // - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO match(IO ma, Func Succ, Func Fail) - where RT : HasIO => - ma.Match(Succ, Fail); - - /// - /// Pattern match the success or failure values and collapse them down to a success value - /// - /// Success value mapping - /// Failure value mapping - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO match(IO ma, Transducer Succ, Transducer Fail) - where RT : HasIO => - ma.Match(Succ, Fail); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO ifFail(IO ma, Func Fail) - where RT : HasIO => - ma.IfFail(Fail); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO ifFail(IO ma, Transducer Fail) - where RT : HasIO => - ma.IfFail(Fail); - - /// - /// Map the failure to a success value - /// - /// Function to map the fail value - /// IO in a success state - [Pure, MethodImpl(Opt.Default)] - public static IO ifFail(IO ma, A Fail) - where RT : HasIO => - ma.IfFail(Fail); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public static IO ifFailIO(IO ma, Func> Fail) - where RT : HasIO => - ma.IfFailIO(Fail); - - /// - /// Map the failure to a new IO effect - /// - /// Function to map the fail value - /// IO that encapsulates that IfFail - [Pure, MethodImpl(Opt.Default)] - public static IO ifFailIO(IO ma, IO Fail) - where RT : HasIO => - ma.IfFailIO(Fail); - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Filter - // - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - [Pure, MethodImpl(Opt.Default)] - public static IO filter(IO ma, Func predicate) - where RT : HasIO => - ma.Filter(predicate); - - /// - /// Only allow values through the effect if the predicate returns `true` for the bound value - /// - /// Predicate to apply to the bound value> - /// Filtered IO - [Pure, MethodImpl(Opt.Default)] - public static IO filter(IO ma, Transducer predicate) - where RT : HasIO => - ma.Filter(predicate); - - - //////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // - // Synchronisation between contexts - // - - /// - /// Make a transducer run on the `SynchronizationContext` that was captured at the start - /// of an `Invoke` call. - /// - /// - /// The transducer receives its input value from the currently running sync-context and - /// then proceeds to run its operation in the captured `SynchronizationContext`: - /// typically a UI context, but could be any captured context. The result of the - /// transducer is the received back on the currently running sync-context. - /// - /// Transducer - /// - public static IO post(IO ma) - where RT : HasIO => - new(Transducer.post(ma.Morphism)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.repeat.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.repeat.cs deleted file mode 100644 index 01c817b42..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.repeat.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -public static partial class Prelude -{ - /// - /// Keeps repeating the computation - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeat(IO ma) - where RT : HasIO => - new(Transducer.repeat(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps repeating the computation, until the scheduler expires - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeat(Schedule schedule, IO ma) - where RT : HasIO => - new(Transducer.repeat(schedule, ma.Morphism)); - - /// - /// Keeps repeating the computation until the predicate returns false - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatWhile( - IO ma, - Func predicate) where RT : HasIO => - new(Transducer.repeatWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatWhile( - Schedule schedule, - IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.repeatWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation until the predicate returns true - /// - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatUntil( - IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.repeatUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps repeating the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for repeating - /// Computation to repeat - /// Runtime - /// Computation bound value type - /// The result of the last invocation of `ma` - public static IO repeatUntil( - Schedule schedule, - IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.repeatUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.retry.cs b/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.retry.cs deleted file mode 100644 index e4f299e9c..000000000 --- a/LanguageExt.Core/Effects/!Probably Going To Delete/IO.RT.E.A/Prelude/IO.Prelude.retry.cs +++ /dev/null @@ -1,87 +0,0 @@ -/* -using System; -using LanguageExt.Effects.Traits; - -namespace LanguageExt; - -public static partial class Prelude -{ - /// - /// Keeps retrying the computation - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retry(IO ma) - where RT : HasIO => - new(Transducer.retry(Schedule.Forever, ma.Morphism)); - - /// - /// Keeps retrying the computation, until the scheduler expires - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retry(Schedule schedule, IO ma) - where RT : HasIO => - new(Transducer.retry(schedule, ma.Morphism)); - - /// - /// Keeps retrying the computation until the predicate returns false - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryWhile( - IO ma, - Func predicate) where RT : HasIO => - new(Transducer.retryWhile(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns false - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryWhile( - Schedule schedule, - IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.retryWhile(schedule, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation until the predicate returns true - /// - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryUntil( - IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.retryUntil(Schedule.Forever, ma.Morphism, predicate)); - - /// - /// Keeps retrying the computation, until the scheduler expires, or the predicate returns true - /// - /// Scheduler strategy for retrying - /// Computation to retry - /// Runtime - /// Computation bound value type - /// The result of the last invocation of ma - public static IO retryUntil( - Schedule schedule, - IO ma, - Func predicate) - where RT : HasIO => - new(Transducer.retryUntil(schedule, ma.Morphism, predicate)); -} -*/ diff --git a/LanguageExt.Core/Effects/IO/ForkIO.cs b/LanguageExt.Core/Effects/IO/ForkIO.cs index 9b2724d15..19e98b885 100644 --- a/LanguageExt.Core/Effects/IO/ForkIO.cs +++ b/LanguageExt.Core/Effects/IO/ForkIO.cs @@ -3,8 +3,8 @@ namespace LanguageExt; /// /// Result of forking an `IO` monad /// -/// An `IO` monad, which if invoked, would cancel the forked IO operation -/// An `IO` monad, which if invoked, would await the result of the forked +/// An `IO` monad, which, if invoked, would cancel the forked IO operation +/// An `IO` monad, which, if invoked, would await the result of the forked /// `IO` operation. Obviously, this mitigates the reasons for forking somewhat, but this struct /// could be passed to another process that does the awaiting - and so still has some value. /// Bound value type diff --git a/LanguageExt.Core/Effects/IO/IO.Prelude.Concurreny.cs b/LanguageExt.Core/Effects/IO/IO.Prelude.Concurreny.cs new file mode 100644 index 000000000..502f22c9b --- /dev/null +++ b/LanguageExt.Core/Effects/IO/IO.Prelude.Concurreny.cs @@ -0,0 +1,466 @@ +using System; +using System.Runtime.CompilerServices; +using LanguageExt.Common; + +namespace LanguageExt; + +public static partial class Prelude +{ + /// + /// Generates a new reference that can be used within a `sync` transaction + /// + /// `Refs` ensure safe shared use of mutable storage locations via a software transactional + /// memory (STM) system. `Refs` are bound to a single storage location for their lifetime, + /// and only allow mutation of that location to occur within a transaction. + /// + /// + /// + /// Transactions (within a `sync(() => ...)`) should be easy to understand if you’ve ever used database + /// transactions - they ensure that all actions on Refs are atomic, consistent, and isolated. + /// + /// * **Atomic** - means that every change to Refs made within a transaction occurs or none do. + /// * **Consistent** - means that each new value can be checked with a validator function before allowing + /// the transaction to commit. + /// * **Isolated** - means that no transaction sees the effects of any other transaction while it is + /// running. + /// + /// Another feature common to STMs is that, should a transaction have a conflict while running, + /// it is automatically retried. The language-ext STM uses multi-version concurrency control for + /// snapshot and serialisable isolation. + /// + /// In practice, this means: + /// + /// All reads of Refs will see a consistent snapshot of the _Ref world_ as of the starting point + /// of the transaction (its 'read point'). The transaction will see any changes it has made. + /// This is called the in-transaction-value. + /// + /// All changes made to Refs during a transaction will appear to occur at a single point in the + /// _Ref world_ timeline (its 'write point'). + /// + /// No changes will have been made by any other transactions to any Refs that have been modified + /// by this transaction. + /// + /// * Readers will never block writers, or other readers. + /// + /// * Writers will never block readers. + /// + /// I/O and other activities with side effects should be avoided in transactions, since transactions + /// will be retried. + /// + /// If a constraint on the validity of a value of a Ref that is being changed depends upon the + /// simultaneous value of a Ref that is not being changed, that second Ref can be protected from + /// modification by running the `sync` transaction with `Isolation.Serialisable`. + /// + /// The language-ext STM is designed to work with the persistent collections (`Map`, `HashMap`, + /// `Seq`, `Lst`, `Set, `HashSet` etc.), and it is strongly recommended that you use the language-ext + /// collections as the values of your Refs. Since all work done in an STM transaction is speculative, + /// it is imperative that there be a low cost to making copies and modifications. Persistent collections + /// have free copies (just use the original, it can’t be changed), and 'modifications' share structure + /// efficiently. In any case: + /// + /// The values placed in Refs must be, or be considered, **immutable**. Otherwise, this library can’t help you. + /// + /// + /// See the [concurrency section](https://github.com/louthy/language-ext/wiki/Concurrency) of the wiki for more info. + /// + /// Initial value of the ref + /// Validator that is called on the ref value just + /// before any transaction is committed (within a `sync`) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO> refIO(A value, Func? validator = null) => + lift(() => Ref(value, validator)); + + /// + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are + /// *written-to within the transaction*. If anything does write to the values used within the transaction, then + /// the transaction is rolled back and retried (using the latest 'world' state). + /// + /// + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used + /// within the transaction, then it is rolled back and retried (using the latest 'world' state). + /// + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write + /// inconsistencies. For example, if you have: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// snapshot(() => x.Value = y.Value + 1); + /// + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. + /// Because `y` was only read-from, not written to. However, this: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// serial(() => x.Value = y.Value + 1); + /// + /// ... would fail if something wrote to `y`. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO atomicIO(Func op, Isolation isolation = Isolation.Snapshot) => + lift(() => atomic(op, isolation)); + + /// + /// Run the op within a new transaction + /// If a transaction is already running, then this becomes part of the parent transaction + /// + /// + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are + /// *written-to within the transaction*. If anything does write to the values used within the transaction, then + /// the transaction is rolled back and retried (using the latest 'world' state). + /// + /// + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used + /// within the transaction, then it is rolled back and retried (using the latest 'world' state). + /// + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write + /// inconsistencies. For example, if you have: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// snapshot(() => x.Value = y.Value + 1); + /// + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. + /// Because `y` was only read-from, not written to. However, this: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// serial(() => x.Value = y.Value + 1); + /// + /// ... would fail if something wrote to `y`. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO atomicIO(Action op, Isolation isolation = Isolation.Snapshot) => + lift(() => atomic(op, isolation)); + + /// + /// Run the op within a new transaction + /// If a transaction is already running, then this becomes part of the parent transaction + /// + /// + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are + /// *written-to within the transaction*. If anything does write to the values used within the transaction, then + /// the transaction is rolled back and retried (using the latest 'world' state). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO snapshotIO(Func op) => + lift(() => snapshot(op)); + + /// + /// Run the op within a new transaction + /// If a transaction is already running, then this becomes part of the parent transaction + /// + /// + /// Snapshot isolation requires that nothing outside the transaction has written to any of the values that are + /// *written-to within the transaction*. If anything does write to the values used within the transaction, then + /// the transaction is rolled back and retried (using the latest 'world' state). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO snapshotIO(Action op) => + lift(() => snapshot(op)); + + /// + /// Run the op within a new transaction + /// If a transaction is already running, then this becomes part of the parent transaction + /// + /// + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used + /// within the transaction, then it is rolled back and retried (using the latest 'world' state). + /// + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write + /// inconsistencies. For example, if you have: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// snapshot(() => x.Value = y.Value + 1); + /// + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. + /// Because `y` was only read-from, not written to. However, this: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// serial(() => x.Value = y.Value + 1); + /// + /// ... would fail if something wrote to `y`. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO serialIO(Func op) => + lift(() => serial(op)); + + /// + /// Run the op within a new transaction + /// If a transaction is already running, then this becomes part of the parent transaction + /// + /// + /// Serialisable isolation requires that nothing outside the transaction has written to any of the values that + /// are *read-from or written-to within the transaction*. If anything does read from or written to the values used + /// within the transaction, then it is rolled back and retried (using the latest 'world' state). + /// + /// It is the strictest form of isolation, and the most likely to conflict; but protects against cross read/write + /// inconsistencies. For example, if you have: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// snapshot(() => x.Value = y.Value + 1); + /// + /// Then something writing to `y` mid-way through the transaction would *not* cause the transaction to fail. + /// Because `y` was only read-from, not written to. However, this: + /// + /// var x = Ref(1); + /// var y = Ref(2); + /// + /// serial(() => x.Value = y.Value + 1); + /// + /// ... would fail if something wrote to `y`. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO serialIO(Action op) => + lift(() => serial(op)); + + /// Swap the old value for the new returned by `f` + /// Must be run within a `sync` transaction + /// + /// `Ref` to process + /// Function to update the `Ref` + /// The value returned from `f` + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO swapIO(Ref r, Func f) => + r.SwapIO(f); + + /// + /// Must be called in a transaction. Sets the in-transaction-value of + /// ref to: + /// + /// `f(in-transaction-value-of-ref)` + /// + /// and returns the in-transaction-value when complete. + /// + /// At the commit point of the transaction, `f` is run *AGAIN* with the + /// most recently committed value: + /// + /// `f(most-recently-committed-value-of-ref)` + /// + /// Thus `f` should be commutative, or, failing that, you must accept + /// last-one-in-wins behavior. + /// + /// Commute allows for more concurrency than just setting the Ref's value + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO commuteIO(Ref r, Func f) => + lift(() => commute(r, f)); + + /// + /// Atoms provide a way to manage shared, synchronous, independent state without + /// locks. + /// + /// Initial value of the atom + /// The constructed Atom + /// + /// The intended use of atom is to hold one an immutable data structure. You change + /// the value by applying a function to the old value. This is done in an atomic + /// manner by `Swap`. + /// + /// Internally, `Swap` reads the current value, applies the function to it, and + /// attempts to `CompareExchange` it in. Since another thread may have changed the + /// value in the intervening time, it may have to retry, and does so in a spin loop. + /// + /// The net effect is that the value will always be the result of the application + /// of the supplied function to a current value, atomically. However, because the + /// function might be called multiple times, it must be free of side effects. + /// + /// Atoms are an efficient way to represent some state that will never need to be + /// coordinated with any other, and for which you wish to make synchronous changes. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO> atomIO(A value) => + lift(() => Atom(value)); + + /// + /// Atoms provide a way to manage shared, synchronous, independent state without + /// locks. + /// + /// Initial value of the atom + /// Function to run on the value after each state change. + /// + /// If the function returns false for any proposed new state, then the `swap` + /// function will return `false`, else it will return `true` on successful setting + /// of the atom's state + /// + /// The constructed Atom or None if the validation faled for the initial + /// `value` + /// + /// The intended use of atom is to hold one an immutable data structure. You change + /// the value by applying a function to the old value. This is done in an atomic + /// manner by `Swap`. + /// + /// Internally, `Swap` reads the current value, applies the function to it, and + /// attempts to `CompareExchange` it in. Since another thread may have changed the + /// value in the intervening time, it may have to retry, and does so in a spin loop. + /// + /// The net effect is that the value will always be the result of the application + /// of the supplied function to a current value, atomically. However, because the + /// function might be called multiple times, it must be free of side effects. + /// + /// Atoms are an efficient way to represent some state that will never need to be + /// coordinated with any other, and for which you wish to make synchronous changes. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO> atomIO(A value, Func validator) => + lift(() => Atom(value, validator) switch + { + { IsSome: true } atom => (Atom)atom, + _ => throw Exceptions.ValidationFailed + }); + + /// + /// Atoms provide a way to manage shared, synchronous, independent state without + /// locks. + /// + /// Metadata to be passed to the validation function + /// Initial value of the atom + /// The constructed Atom + /// + /// The intended use of atom is to hold one an immutable data structure. You change + /// the value by applying a function to the old value. This is done in an atomic + /// manner by `Swap`. + /// + /// Internally, `Swap` reads the current value, applies the function to it, and + /// attempts to `CompareExchange` it in. Since another thread may have changed the + /// value in the intervening time, it may have to retry, and does so in a spin loop. + /// + /// The net effect is that the value will always be the result of the application + /// of the supplied function to a current value, atomically. However, because the + /// function might be called multiple times, it must be free of side effects. + /// + /// Atoms are an efficient way to represent some state that will never need to be + /// coordinated with any other, and for which you wish to make synchronous changes. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO> atomIO(M metadata, A value) => + lift(() => Atom(metadata, value)); + + /// + /// Atoms provide a way to manage shared, synchronous, independent state without + /// locks. + /// + /// Metadata to be passed to the validation function + /// Initial value of the atom + /// Function to run on the value after each state change. + /// + /// If the function returns false for any proposed new state, then the `swap` + /// function will return `false`, else it will return `true` on successful setting + /// of the atom's state + /// + /// The constructed Atom or None if the validation faled for the initial + /// `value` + /// + /// The intended use of atom is to hold one an immutable data structure. You change + /// the value by applying a function to the old value. This is done in an atomic + /// manner by `Swap`. + /// + /// Internally, `Swap` reads the current value, applies the function to it, and + /// attempts to `CompareExchange` it in. Since another thread may have changed the + /// value in the intervening time, it may have to retry, and does so in a spin loop. + /// + /// The net effect is that the value will always be the result of the application + /// of the supplied function to a current value, atomically. However, because the + /// function might be called multiple times, it must be free of side effects. + /// + /// Atoms are an efficient way to represent some state that will never need to be + /// coordinated with any other, and for which you wish to make synchronous changes. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static IO> atomIO(M metadata, A value, Func validator) => + lift(() => Atom(metadata, value, validator) switch + { + { IsSome: true } atom => (Atom)atom, + _ => throw Exceptions.ValidationFailed + }); + + /// + /// Atomically updates the value by passing the old value to `f` and updating + /// the atom with the result. Note: `f` may be called multiple times, so it + /// should be free of side effects. + /// + /// Function to update the atom + /// IO in a success state, with the result of the invocation of `f`, if the swap succeeded and its + /// validation passed. Failure state otherwise + public static IO swapIO(Atom ma, Func f) => + ma.SwapIO(f); + + /// + /// Atomically updates the value by passing the old value to `f` and updating + /// the atom with the result. Note: `f` may be called multiple times, so it + /// should be free of side effects. + /// + /// Function to update the atom + /// IO in a success state, with the result of the invocation of `f`, if the swap succeeded and its + /// validation passed. Failure state otherwise + public static IO swapIO(Atom ma, Func f) => + ma.SwapIO(f); + + /// + /// Retrieve an IO computation that on each invocation will snapshot of the + /// value in an atom + /// + /// Atom to snapshot + /// Value type + /// IO representation of the snapshot + public static IO valueIO(Atom ma) => + ma.ValueIO; + + /// + /// Retrieve an IO computation that on each invocation will snapshot of the + /// value in an atom + /// + /// Atom to snapshot + /// Value type + /// IO representation of the snapshot + public static IO valueIO(Atom ma) => + ma.ValueIO; + + /// + /// Write a value to an atom. + /// + /// + /// Note, this can be dangerous and is usually better to use `swapIO` if the + /// `value` is derived from a snapshot of the atom's value (via `valueIO`). + /// + /// The computation is better run inside the swap operation for atomic consistency. + /// + /// However, using `writeIO` is reasonable if this is simply a forceful overwrite + /// of the atomic value without any dependency on the previous state. + /// + /// Atom to write + /// Value type + /// IO representation of the write operation + public static IO writeIO(Atom ma, A value) => + ma.SwapIO(_ => value); + + /// + /// Write a value to an atom. + /// + /// + /// Note, this can be dangerous and is usually better to use `swapIO` if the + /// `value` is derived from a snapshot of the atom's value (via `valueIO`). + /// + /// The computation is better run inside the swap operation for atomic consistency. + /// + /// However, using `writeIO` is reasonable if this is simply a forceful overwrite + /// of the atomic value without any dependency on the previous state. + /// + /// Atom to write + /// Value type + /// IO representation of the write operation + public static IO writeIO(Atom ma, A value) => + ma.SwapIO((_, _) => value); +} diff --git a/LanguageExt.Core/Immutable Collections/StreamT/MList.cs b/LanguageExt.Core/Immutable Collections/StreamT/MList.cs index fe376b130..ed2ff543d 100644 --- a/LanguageExt.Core/Immutable Collections/StreamT/MList.cs +++ b/LanguageExt.Core/Immutable Collections/StreamT/MList.cs @@ -26,7 +26,7 @@ public K> Append(K> ys) { MNil => ys, MCons (var h, var t) => M.Pure(Cons(h, t.Append(ys))), - MIter (var h, _) iter => M.Pure(Cons(h, iter.TailToMList().Append(ys))), + MIter (var h, _) iter => M.Pure(Cons(h, iter.TailM().Append(ys))), _ => throw new NotSupportedException() }; } @@ -55,7 +55,7 @@ public override MList Map(Func f) => public MList ToCons() => new MCons(Head, StreamEnumerableT.Lift(Tail).runListT); - public K> TailToMList() => + public K> TailM() => StreamEnumerableT.Lift(Tail).runListT; } diff --git a/LanguageExt.Core/Immutable Collections/StreamT/StreamT.DSL.Enumerable.cs b/LanguageExt.Core/Immutable Collections/StreamT/StreamT.DSL.Enumerable.cs index 874ebbf46..3ed24945a 100644 --- a/LanguageExt.Core/Immutable Collections/StreamT/StreamT.DSL.Enumerable.cs +++ b/LanguageExt.Core/Immutable Collections/StreamT/StreamT.DSL.Enumerable.cs @@ -19,8 +19,7 @@ public static StreamT Lift(IEnumerator iter) { if (iter.MoveNext()) { - return new StreamEnumerableItemT( - M.Pure(MList.Iter(iter.Current, iter))); + return new StreamEnumerableItemT(M.Pure(MList.Iter(iter.Current, iter))); } else { diff --git a/LanguageExt.Core/Immutable Collections/StreamT/StreamT.Extensions.cs b/LanguageExt.Core/Immutable Collections/StreamT/StreamT.Extensions.cs index 2336131ef..41f90b176 100644 --- a/LanguageExt.Core/Immutable Collections/StreamT/StreamT.Extensions.cs +++ b/LanguageExt.Core/Immutable Collections/StreamT/StreamT.Extensions.cs @@ -34,7 +34,7 @@ public static K> Flatten(this MList>> mma) { MNil>> => M.Pure(MNil.Default), MCons>> (var h, var t) => h.Append(t.Flatten()), - MIter>> (var h, _) iter => h.Append(iter.TailToMList().Flatten()), + MIter>> (var h, _) iter => h.Append(iter.TailM().Flatten()), _ => throw new NotSupportedException() }; diff --git a/LanguageExt.Core/Immutable Collections/StreamT/StreamT.cs b/LanguageExt.Core/Immutable Collections/StreamT/StreamT.cs index 098c749f3..97bc15c2b 100644 --- a/LanguageExt.Core/Immutable Collections/StreamT/StreamT.cs +++ b/LanguageExt.Core/Immutable Collections/StreamT/StreamT.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using LanguageExt.Common; using LanguageExt.Traits; namespace LanguageExt; @@ -28,11 +29,18 @@ public K Iter() => Option<(A Head, StreamT Tail)>.Some((h, new StreamMainT(t))), MIter(var h, _) iter => - Option<(A Head, StreamT Tail)>.Some((h, new StreamMainT(iter.TailToMList()))), + Option<(A Head, StreamT Tail)>.Some((h, new StreamMainT(iter.TailM()))), _ => throw new NotSupportedException() }); + public K Head() => + Run().Map(opt => opt switch + { + { IsSome: true} => opt.Value.Item1, + _ => throw Exceptions.None + }); + public static StreamT Pure(A value) => new StreamPureT(value); @@ -108,4 +116,19 @@ public StreamT SelectMany(Func> bind, Func project public StreamT SelectMany(Func> bind, Func project) => this.Kind().Bind(x => bind(x).Map(y => project(x, y))).As(); + + public static implicit operator StreamT(Pure value) => + Pure(value.Value); + + public static implicit operator StreamT(Iterable value) => + Lift(value); + + public static implicit operator StreamT(IO value) => + LiftIO(value); + + public static StreamT operator +(StreamT lhs, StreamT rhs) => + lhs.Combine(rhs); + + public static StreamT operator >> (StreamT lhs, StreamT rhs) => + lhs.Bind(_ => rhs); } diff --git a/LanguageExt.Core/Utility/Box.cs b/LanguageExt.Core/Utility/Box.cs index 30720a8b3..1ba9c38b4 100644 --- a/LanguageExt.Core/Utility/Box.cs +++ b/LanguageExt.Core/Utility/Box.cs @@ -34,7 +34,7 @@ static Func GetValueClass() { if (ILCapability.Available) { - var dynamic = new DynamicMethod("GetValue_Class", typeof(A), new[] {typeof(object)}, typeof(A).Module, true); + var dynamic = new DynamicMethod("GetValue_Class", typeof(A), [typeof(object)], typeof(A).Module, true); var il = dynamic.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); @@ -44,7 +44,7 @@ static Func GetValueClass() } else { - return (object x) => (A)x; + return x => (A)x; } } diff --git a/LanguageExt.Tests/AtomTests.cs b/LanguageExt.Tests/AtomTests.cs index 69a80c2fb..e6eda481f 100644 --- a/LanguageExt.Tests/AtomTests.cs +++ b/LanguageExt.Tests/AtomTests.cs @@ -18,17 +18,6 @@ public void ConstructAndSwap() Debug.Assert(atom == Set("A", "B", "C", "D", "E", "F")); } - - [Fact] - public void ConstructWithMetaDataAndSwap() - { - var atom = Atom(Set(1, 2, 3)); - - atom.Swap(4, 5, (x, y, old) => old.Add(x).Add(y)); - - Debug.Assert(atom == Set(1, 2, 3, 4, 5)); - } - [Fact] public void AtomSeqEnumeration() { diff --git a/Samples/Streams/Console.cs b/Samples/Streams/Console.cs index b3f8daccb..aa5b3ac42 100644 --- a/Samples/Streams/Console.cs +++ b/Samples/Streams/Console.cs @@ -9,6 +9,9 @@ public static class Console public static IO writeLine(string line) => lift(() => System.Console.WriteLine(line)); + public static IO write(string text) => + lift(() => System.Console.Write(text)); + public static IO readLine => lift(() => System.Console.ReadLine() ?? ""); diff --git a/Samples/Streams/Grouping.cs b/Samples/Streams/Grouping.cs new file mode 100644 index 000000000..a0fcbacd5 --- /dev/null +++ b/Samples/Streams/Grouping.cs @@ -0,0 +1,48 @@ +using LanguageExt; +using static LanguageExt.Prelude; + +namespace Streams; + +/// +/// Based on 'Grouping effects' from 'ListT done right' +/// +/// https://wiki.haskell.org/ListT_done_right +/// +public static class Grouping +{ + public static IO run => + from _1 in runTestIO("test 1", test1) + from _2 in runTestIO("test 2", test2) + select unit; + + static IO runTestIO(string name, StreamT test) => + from _1 in runTest(name, test1).Iter().As() + from _2 in Console.writeLine("\n") + select unit; + + static StreamT runTest(string name, StreamT test) => + from t in Console.writeLine($"{name}") + from r in test + from _ in Console.write($"{r} ") + where false + select unit; + + static StreamT test1 = + from r in atomIO(0) + from n in next(r) + next(r) >> (next(r) + next(r) >> next(r) + next(r)) + select n; + + static StreamT test2 = + from r in atomIO(0) + from n in (next(r) + next(r) >> next(r) + next(r)) >> next(r) + next(r) + select n; + + static StreamT next(Atom atom) => + from x in valueIO(atom) + from _ in writeIO(atom, x + 1) + select x; + + static StreamT next1(Atom atom) => + atom.SwapIO(x => x + 1); + +} diff --git a/Samples/Streams/Menu.cs b/Samples/Streams/Menu.cs index 3af1d8154..56d6d76bf 100644 --- a/Samples/Streams/Menu.cs +++ b/Samples/Streams/Menu.cs @@ -13,6 +13,7 @@ from ky in Console.readKey ConsoleKey.D1 => CountForever.run, ConsoleKey.D2 => CountForeverAsync.run, ConsoleKey.D3 => SumOfSquares.run, + ConsoleKey.D4 => Grouping.run, _ => unitIO } from _1 in run @@ -20,8 +21,9 @@ from _1 in run static IO introduction => from _1 in Console.writeLine("1. Count forever sample") - from _2 in Console.writeLine("2. Count forever (async) sample") + from _2 in Console.writeLine("2. Count forever sample (async, with per-item delay)") from _3 in Console.writeLine("3. Sum of squares example") + from _4 in Console.writeLine("4. Grouping test") from __ in Console.writeLine("Enter a number for the example you wish to run") select unit; } diff --git a/Samples/Streams/SumOfSquares.cs b/Samples/Streams/SumOfSquares.cs index 9648367c1..57b6bf152 100644 --- a/Samples/Streams/SumOfSquares.cs +++ b/Samples/Streams/SumOfSquares.cs @@ -5,6 +5,11 @@ namespace Streams; +/// +/// Based on 'Sum of squares' from 'ListT done right' +/// +/// https://wiki.haskell.org/ListT_done_right +/// public class SumOfSquares { public static IO run =>