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
///
- /// 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