diff --git a/arrow-libs/core/arrow-atomic/api/arrow-atomic.api b/arrow-libs/core/arrow-atomic/api/arrow-atomic.api new file mode 100644 index 00000000000..994ac619337 --- /dev/null +++ b/arrow-libs/core/arrow-atomic/api/arrow-atomic.api @@ -0,0 +1,20 @@ +public abstract interface class arrow/atomic/Atomic { + public abstract fun compareAndSet (Ljava/lang/Object;Ljava/lang/Object;)Z + public abstract fun getAndSet (Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun getValue ()Ljava/lang/Object; + public abstract fun setAndGet (Ljava/lang/Object;)Ljava/lang/Object; + public abstract fun setValue (Ljava/lang/Object;)V +} + +public final class arrow/atomic/AtomicActual { + public static final fun Atomic (Ljava/lang/Object;)Larrow/atomic/Atomic; +} + +public final class arrow/atomic/AtomicKt { + public static final fun getAndUpdate (Larrow/atomic/Atomic;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static final fun loop (Larrow/atomic/Atomic;Lkotlin/jvm/functions/Function1;)Ljava/lang/Void; + public static final fun tryUpdate (Larrow/atomic/Atomic;Lkotlin/jvm/functions/Function1;)Z + public static final fun update (Larrow/atomic/Atomic;Lkotlin/jvm/functions/Function1;)V + public static final fun updateAndGet (Larrow/atomic/Atomic;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +} + diff --git a/arrow-libs/core/arrow-atomic/build.gradle.kts b/arrow-libs/core/arrow-atomic/build.gradle.kts new file mode 100644 index 00000000000..5ef68dd39a6 --- /dev/null +++ b/arrow-libs/core/arrow-atomic/build.gradle.kts @@ -0,0 +1,47 @@ +plugins { + id(libs.plugins.kotlin.multiplatform.get().pluginId) + alias(libs.plugins.arrowGradleConfig.kotlin) + alias(libs.plugins.arrowGradleConfig.publish) +} + +apply(plugin = "io.kotest.multiplatform") +apply(from = property("TEST_COVERAGE")) +apply(from = property("ANIMALSNIFFER_MPP")) + +val enableCompatibilityMetadataVariant = + providers.gradleProperty("kotlin.mpp.enableCompatibilityMetadataVariant") + .forUseAtConfigurationTime().orNull?.toBoolean() == true + +if (enableCompatibilityMetadataVariant) { + tasks.withType().configureEach { + exclude("**/*") + } +} + +kotlin { + sourceSets { + commonMain { + dependencies { + api(libs.kotlin.stdlibCommon) + } + } + + jvmMain { + dependencies { + implementation(libs.kotlin.stdlibJDK8) + } + } + + jvmTest { + dependencies { + implementation(projects.arrowFxCoroutines) + } + } + + jsMain { + dependencies { + implementation(libs.kotlin.stdlibJS) + } + } + } +} diff --git a/arrow-libs/core/arrow-atomic/gradle.properties b/arrow-libs/core/arrow-atomic/gradle.properties new file mode 100644 index 00000000000..e9b3987e4af --- /dev/null +++ b/arrow-libs/core/arrow-atomic/gradle.properties @@ -0,0 +1,4 @@ +# Maven publishing configuration +pom.name=Arrow Atomic +# Build configuration +kapt.incremental.apt=false diff --git a/arrow-libs/core/arrow-atomic/knit.properties b/arrow-libs/core/arrow-atomic/knit.properties new file mode 100644 index 00000000000..0927cad52c1 --- /dev/null +++ b/arrow-libs/core/arrow-atomic/knit.properties @@ -0,0 +1,5 @@ +knit.package=arrow.atomic.examples +knit.dir=src/jvmTest/kotlin/examples/ + +test.package=arrow.atomic.examples.test +test.dir=src/jvmTest/kotlin/examples/autogenerated/ diff --git a/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/Atomic.kt b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/Atomic.kt new file mode 100644 index 00000000000..b4824ff0ce4 --- /dev/null +++ b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/Atomic.kt @@ -0,0 +1,74 @@ +package arrow.atomic + +public expect fun Atomic(initialValue: A): Atomic + +/** + * [Atomic] value of [A]. + * + * ```kotlin + * import arrow.atomic.Atomic + * import arrow.atomic.update + * import arrow.fx.coroutines.parTraverse + * suspend fun main() { + * val count = Atomic(0) + * (0 until 20_000).parTraverse { + * count.update(Int::inc) + * } + * println(count.value) + * } + * ``` + * + * + * [Atomic] also offers some other interesting operators such as [loop], [update], [tryUpdate], etc. + */ +public interface Atomic { + public var value: A + public fun getAndSet(value: A): A + public fun setAndGet(value: A): A + public fun compareAndSet(expected: A, new: A): Boolean +} + +/** + * Infinite loop that reads this atomic variable and performs the specified [action] on its value. + */ +public inline fun Atomic.loop(action: (V) -> Unit): Nothing { + while (true) { + action(value) + } +} + +public fun Atomic.tryUpdate(function: (V) -> V): Boolean { + val cur = value + val upd = function(cur) + return compareAndSet(cur, upd) +} + +public inline fun Atomic.update(function: (V) -> V) { + while (true) { + val cur = value + val upd = function(cur) + if (compareAndSet(cur, upd)) return + } +} + +/** + * Updates variable atomically using the specified [function] of its value and returns its old value. + */ +public inline fun Atomic.getAndUpdate(function: (V) -> V): V { + while (true) { + val cur = value + val upd = function(cur) + if (compareAndSet(cur, upd)) return cur + } +} + +/** + * Updates variable atomically using the specified [function] of its value and returns its new value. + */ +public inline fun Atomic.updateAndGet(function: (V) -> V): V { + while (true) { + val cur = value + val upd = function(cur) + if (compareAndSet(cur, upd)) return upd + } +} diff --git a/arrow-libs/core/arrow-atomic/src/jsMain/kotlin/arrow/atomic/Atomic.kt b/arrow-libs/core/arrow-atomic/src/jsMain/kotlin/arrow/atomic/Atomic.kt new file mode 100644 index 00000000000..b39be577690 --- /dev/null +++ b/arrow-libs/core/arrow-atomic/src/jsMain/kotlin/arrow/atomic/Atomic.kt @@ -0,0 +1,38 @@ +package arrow.atomic + +public actual fun Atomic(initialValue: A): Atomic = + AtomicRef(initialValue) + +private class AtomicRef(private var internalValue: V) : Atomic { + + /** + * Compare current value with expected and set to new if they're the same. Note, 'compare' is checking + * the actual object id, not 'equals'. + */ + override fun compareAndSet(expected: V, new: V): Boolean { + return if (expected === internalValue) { + internalValue = new + true + } else { + false + } + } + + override fun getAndSet(value: V): V { + val oldValue = internalValue + internalValue = value + return oldValue + } + + override fun setAndGet(value: V): V { + this.internalValue = value + return value + } + + + override var value: V + get() = internalValue + set(value) { + internalValue = value + } +} diff --git a/arrow-libs/core/arrow-atomic/src/jvmMain/kotlin/arrow/atomic/Atomic.kt b/arrow-libs/core/arrow-atomic/src/jvmMain/kotlin/arrow/atomic/Atomic.kt new file mode 100644 index 00000000000..73b9b6a6118 --- /dev/null +++ b/arrow-libs/core/arrow-atomic/src/jvmMain/kotlin/arrow/atomic/Atomic.kt @@ -0,0 +1,26 @@ +@file:JvmName("AtomicActual") + +package arrow.atomic + +import java.util.concurrent.atomic.AtomicReference + +public actual fun Atomic(initialValue: A): Atomic = + AtomicRef(AtomicReference(initialValue)) + +private class AtomicRef constructor(private val atom: AtomicReference) : Atomic { + + override var value: A + get() = atom.get() + set(value) { + atom.set(value) + } + + override fun compareAndSet(expected: A, new: A): Boolean = atom.compareAndSet(expected, new) + + override fun getAndSet(value: A): A = atom.getAndSet(value) + + override fun setAndGet(value: A): A { + atom.set(value) + return value + } +} diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-01.kt b/arrow-libs/core/arrow-atomic/src/jvmTest/kotlin/examples/example-atomic-01.kt similarity index 54% rename from arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-01.kt rename to arrow-libs/core/arrow-atomic/src/jvmTest/kotlin/examples/example-atomic-01.kt index b5daa8841cf..418d6c41d7a 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-01.kt +++ b/arrow-libs/core/arrow-atomic/src/jvmTest/kotlin/examples/example-atomic-01.kt @@ -1,13 +1,13 @@ // This file was automatically generated from Atomic.kt by Knit tool. Do not edit. -package arrow.fx.coroutines.examples.exampleAtomic01 - -import arrow.fx.coroutines.* +package arrow.atomic.examples.exampleAtomic01 +import arrow.atomic.Atomic +import arrow.atomic.update +import arrow.fx.coroutines.parTraverse suspend fun main() { val count = Atomic(0) - (0 until 20_000).parTraverse { count.update(Int::inc) } - println(count.get()) + println(count.value) } diff --git a/arrow-libs/core/arrow-atomic/src/nativeMain/kotlin/arrow/atomic/Atomic.kt b/arrow-libs/core/arrow-atomic/src/nativeMain/kotlin/arrow/atomic/Atomic.kt new file mode 100644 index 00000000000..1714904e917 --- /dev/null +++ b/arrow-libs/core/arrow-atomic/src/nativeMain/kotlin/arrow/atomic/Atomic.kt @@ -0,0 +1,35 @@ +package arrow.atomic + +import kotlin.native.concurrent.AtomicReference +import kotlin.native.concurrent.freeze +import kotlin.native.concurrent.isFrozen + +public actual fun Atomic(initialValue: A): Atomic = + AtomicRef(AtomicReference(initialValue.freeze())) + +private class AtomicRef(private val atom: AtomicReference): Atomic { + + override fun getAndSet(value: V): V { + if (atom.isFrozen) value.freeze() + while (true) { + val cur = atom.value + if (cur === value) return cur + if (atom.compareAndSwap(cur, value) === cur) return cur + } + } + + override fun compareAndSet(expected: V, new: V): Boolean = + atom.compareAndSet(expected, new.freeze()) + + override var value: V + get() = atom.value + set(value) { + atom.value = value.freeze() + } + + override fun setAndGet(value: V): V { + this.value = value + return value + } +} + diff --git a/arrow-libs/core/arrow-core/api/arrow-core.api b/arrow-libs/core/arrow-core/api/arrow-core.api index 04c5171a396..8c684b8f59a 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.api @@ -2578,13 +2578,6 @@ public final class arrow/core/computations/result { public final fun invoke-IoAF18A (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; } -public final class arrow/core/continuations/AtomicRefKt { - public static final fun getAndUpdate (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; - public static final fun loop (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)Ljava/lang/Void; - public static final fun update (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)V - public static final fun updateAndGet (Ljava/util/concurrent/atomic/AtomicReference;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; -} - public final class arrow/core/continuations/DefaultRaise : arrow/core/continuations/Raise { public fun ()V public fun bind (Larrow/core/Either;)Ljava/lang/Object; @@ -2603,6 +2596,29 @@ public final class arrow/core/continuations/DefaultRaise : arrow/core/continuati public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public final class arrow/core/continuations/DefaultStateRaise : arrow/atomic/Atomic, arrow/core/continuations/Raise, arrow/core/continuations/StateRaise { + public fun (Larrow/atomic/Atomic;Larrow/core/continuations/Raise;)V + public fun bind (Larrow/core/Either;)Ljava/lang/Object; + public fun bind (Larrow/core/Option;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun bind (Larrow/core/Validated;)Ljava/lang/Object; + public fun bind (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun bind (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun bind (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun compareAndSet (Ljava/lang/Object;Ljava/lang/Object;)Z + public fun getAndSet (Ljava/lang/Object;)Ljava/lang/Object; + public fun getValue ()Ljava/lang/Object; + public fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun raise (Ljava/lang/Object;)Ljava/lang/Object; + public fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public fun setAndGet (Ljava/lang/Object;)Ljava/lang/Object; + public fun setValue (Ljava/lang/Object;)V +} + public final class arrow/core/continuations/Effect { public static final fun _fold (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public static final fun _foldOrThrow (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; @@ -2648,7 +2664,7 @@ public final class arrow/core/continuations/EffectKt { } public final class arrow/core/continuations/IorRaise : arrow/core/continuations/Raise, arrow/typeclasses/Semigroup { - public fun (Larrow/typeclasses/Semigroup;Larrow/core/continuations/Raise;)V + public fun (Larrow/typeclasses/Semigroup;Larrow/core/continuations/StateRaise;)V public fun bind (Larrow/core/Either;)Ljava/lang/Object; public final fun bind (Larrow/core/Ior;)Ljava/lang/Object; public fun bind (Larrow/core/Option;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; @@ -2659,8 +2675,7 @@ public final class arrow/core/continuations/IorRaise : arrow/core/continuations/ public fun catch (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun catch (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun combine (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; - public final fun getLeftState ()Ljava/util/concurrent/atomic/AtomicReference; - public final fun invoke (Lkotlin/jvm/functions/Function1;)Larrow/core/Ior; + public final fun getEffect ()Larrow/core/continuations/StateRaise; public fun invoke (Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; public fun invoke (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun maybeCombine (Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object; @@ -2669,7 +2684,6 @@ public final class arrow/core/continuations/IorRaise : arrow/core/continuations/ public fun recover (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public fun recover (Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun setLeftState (Ljava/util/concurrent/atomic/AtomicReference;)V } public final class arrow/core/continuations/NullableRaise : arrow/core/continuations/Raise { @@ -2847,6 +2861,29 @@ public final class arrow/core/continuations/ResultRaise : arrow/core/continuatio public final synthetic fun unbox-impl ()Larrow/core/continuations/Raise; } +public abstract interface class arrow/core/continuations/StateRaise : arrow/atomic/Atomic, arrow/core/continuations/Raise { +} + +public final class arrow/core/continuations/StateRaise$DefaultImpls { + public static fun bind (Larrow/core/continuations/StateRaise;Larrow/core/Either;)Ljava/lang/Object; + public static fun bind (Larrow/core/continuations/StateRaise;Larrow/core/Option;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun bind (Larrow/core/continuations/StateRaise;Larrow/core/Validated;)Ljava/lang/Object; + public static fun bind (Larrow/core/continuations/StateRaise;Ljava/lang/Object;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static fun bind (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static fun bind (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static fun catch (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun catch (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static fun invoke (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; + public static fun invoke (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static fun recover (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; + public static fun recover (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static fun recover (Larrow/core/continuations/StateRaise;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class arrow/core/continuations/StateRaiseKt { + public static final fun fold (Ljava/lang/Object;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)Ljava/lang/Object; +} + public abstract interface class arrow/typeclasses/Monoid : arrow/typeclasses/Semigroup { public static final field Companion Larrow/typeclasses/Monoid$Companion; public static fun Boolean ()Larrow/typeclasses/Monoid; diff --git a/arrow-libs/core/arrow-core/build.gradle.kts b/arrow-libs/core/arrow-core/build.gradle.kts index 5c3541f2696..c5a58b395cc 100644 --- a/arrow-libs/core/arrow-core/build.gradle.kts +++ b/arrow-libs/core/arrow-core/build.gradle.kts @@ -25,6 +25,7 @@ kotlin { commonMain { dependencies { api(projects.arrowContinuations) + api(projects.arrowAtomic) api(projects.arrowAnnotations) api(libs.kotlin.stdlibCommon) } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/AtomicRef.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/AtomicRef.kt deleted file mode 100644 index fb7e6d61e6c..00000000000 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/AtomicRef.kt +++ /dev/null @@ -1,52 +0,0 @@ -package arrow.core.continuations - -public expect class AtomicRef(initialValue: V) { - public fun get(): V - public fun set(value: V) - public fun getAndSet(value: V): V - - /** - * Compare current value with expected and set to new if they're the same. Note, 'compare' is checking - * the actual object id, not 'equals'. - */ - public fun compareAndSet(expected: V, new: V): Boolean -} - -/** - * Infinite loop that reads this atomic variable and performs the specified [action] on its value. - */ -public inline fun AtomicRef.loop(action: (V) -> Unit): Nothing { - while (true) { - action(get()) - } -} - -public inline fun AtomicRef.update(function: (V) -> V) { - while (true) { - val cur = get() - val upd = function(cur) - if (compareAndSet(cur, upd)) return - } -} - -/** - * Updates variable atomically using the specified [function] of its value and returns its old value. - */ -public inline fun AtomicRef.getAndUpdate(function: (V) -> V): V { - while (true) { - val cur = get() - val upd = function(cur) - if (compareAndSet(cur, upd)) return cur - } -} - -/** - * Updates variable atomically using the specified [function] of its value and returns its new value. - */ -public inline fun AtomicRef.updateAndGet(function: (V) -> V): V { - while (true) { - val cur = get() - val upd = function(cur) - if (compareAndSet(cur, upd)) return upd - } -} diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Builders.kt index 46b8e8ea3b8..a242ac30fa0 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Builders.kt @@ -4,13 +4,15 @@ package arrow.core.continuations +import arrow.atomic.updateAndGet import arrow.core.Either -import arrow.core.EmptyValue import arrow.core.Ior import arrow.core.None import arrow.core.Option import arrow.core.Some +import arrow.core.getOrElse import arrow.core.identity +import arrow.core.orElse import arrow.typeclasses.Semigroup import kotlin.contracts.ExperimentalContracts import kotlin.contracts.contract @@ -32,7 +34,13 @@ public inline fun option(action: OptionRaise.() -> A): Option = fold({ action(OptionRaise(this)) }, ::identity, ::Some) public inline fun ior(semigroup: Semigroup, @BuilderInference action: IorRaise.() -> A): Ior = - fold({ IorRaise(semigroup, this).invoke(action) }, { Ior.Left(it) }, ::identity) + fold, E, A, Ior>( + None, + { action(IorRaise(semigroup, this)) }, + { _, e -> throw e }, + { state, e -> Ior.Left(state.getOrElse { e }) }, + { state, a -> state.fold({ Ior.Right(a) }, { Ior.Both(it, a) }) } + ) @JvmInline public value class NullableRaise(private val cont: Raise) : Raise { @@ -70,12 +78,12 @@ public value class OptionRaise(private val cont: Raise) : Raise { } } -public class IorRaise @PublishedApi internal constructor(semigroup: Semigroup, private val effect: Raise) : - Raise, Semigroup by semigroup { - - // TODO this is a mess... +public class IorRaise @PublishedApi internal constructor( + semigroup: Semigroup, @PublishedApi - internal var leftState: AtomicRef = AtomicRef(EmptyValue) + internal val effect: StateRaise, E>, +) : Raise, Semigroup by semigroup { + override fun raise(r: E): B = effect.raise(combine(r)) public fun Ior.bind(): B = @@ -87,18 +95,9 @@ public class IorRaise @PublishedApi internal constructor(semigroup: Semigroup rightValue } } - - @PublishedApi - internal inline operator fun invoke(action: IorRaise.() -> A): Ior { - val res = action(this) - val leftState = leftState.get() - return if (leftState === EmptyValue) Ior.Right(res) - else Ior.Both(EmptyValue.unbox(leftState), res) - } - - @Suppress("UNCHECKED_CAST") + private fun combine(other: E): E = - leftState.updateAndGet { state -> - if (state === EmptyValue) other else EmptyValue.unbox(state).combine(other) - } as E + effect.updateAndGet { state -> + state.map { e -> e.combine(other) }.orElse { Some(other) } + }.getOrElse { other } } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Effect.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Effect.kt index dc083ce81ad..d9de40f4727 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Effect.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Effect.kt @@ -313,7 +313,7 @@ import kotlin.jvm.JvmMultifileClass * * Note: * Handling errors can also be done with `try/catch` but this is **not recommended**, it uses `CancellationException` which is used to cancel `Coroutine`s and is advised not to capture in Kotlin. - * The `CancellationException` from `Effect` is `ShiftCancellationException`, this a public type, thus can be distinguished from any other `CancellationException` if necessary. + * The `CancellationException` from `Effect` is `RaiseCancellationException`, this a public type, thus can be distinguished from any other `CancellationException` if necessary. * * ## Structured Concurrency * diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/ErrorHandlers.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/ErrorHandlers.kt index 3012d60950c..82d2e30a86b 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/ErrorHandlers.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/ErrorHandlers.kt @@ -10,9 +10,9 @@ import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName /** - * Catch the shifted value [E] of the `Effect`. + * Catch the raised value [E] of the `Effect`. * You can either return a value a new value of [A], - * or short-circuit the effect by shifting with a value of [E], + * or short-circuit the effect by raising with a value of [E], * or raise an exception into [suspend]. * * ```kotlin @@ -30,13 +30,13 @@ import kotlin.jvm.JvmName * ``` * */ -public infix fun Effect.recover(@BuilderInference resolve: suspend Raise.(shifted: E) -> A): Effect = +public infix fun Effect.recover(@BuilderInference resolve: suspend Raise.(raised: E) -> A): Effect = effect { recover(resolve) } /** * Catch any unexpected exceptions, and [resolve] them. * You can either return a value a new value of [A], - * or short-circuit the effect by shifting with a value of [E], + * or short-circuit the effect by raising with a value of [E], * or raise an exception into [suspend]. * * ```kotlin @@ -98,7 +98,7 @@ public fun Effect.catch(): Effect> = } } -public infix fun EagerEffect.recover(@BuilderInference resolve: Raise.(shifted: E) -> A): EagerEffect = +public infix fun EagerEffect.recover(@BuilderInference resolve: Raise.(raised: E) -> A): EagerEffect = eagerEffect { recover(resolve) } public infix fun EagerEffect.catch(@BuilderInference recover: Raise.(throwable: Throwable) -> A): EagerEffect = diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Fold.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Fold.kt index 495e14de5fb..bbae0759511 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Fold.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Fold.kt @@ -63,7 +63,7 @@ public inline fun fold( } } -/** Returns the shifted value, rethrows the CancellationException if not our scope */ +/** Returns the raised value, rethrows the CancellationException if not our scope */ @PublishedApi internal fun CancellationException.raisedOrRethrow(raise: DefaultRaise): R = if (this is RaiseCancellationException && this.raise === raise) _raised as R @@ -75,6 +75,6 @@ internal class DefaultRaise : Raise { override fun raise(r: Any?): B = throw RaiseCancellationException(r, this) } -/** CancellationException is required to cancel coroutines when shifting from within them. */ +/** CancellationException is required to cancel coroutines when raising from within them. */ private class RaiseCancellationException(val _raised: Any?, val raise: Raise) : CancellationException("Raised Continuation") diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Mappers.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Mappers.kt index 9df1bb9e3b6..19bd59d0fd9 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Mappers.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Mappers.kt @@ -24,7 +24,7 @@ public fun EagerEffect.toValidated(): Validated = fold({ Vali public suspend fun Effect.toIor(): Ior = fold({ Ior.Left(it) }) { Ior.Right(it) } public fun EagerEffect.toIor(): Ior = fold({ Ior.Left(it) }) { Ior.Right(it) } -/** Run the [Effect] by returning [A], or `null` if shifted with [E]. */ +/** Run the [Effect] by returning [A], or `null` if raised with [E]. */ public suspend fun Effect.orNull(): A? = fold({ _: E -> null }) { it } public fun EagerEffect.orNull(): A? = fold({ _: E -> null }) { it } @@ -32,14 +32,16 @@ public fun EagerEffect.orNull(): A? = fold({ _: E -> null }) { it } public suspend fun Effect.toOption(orElse: suspend (E) -> Option): Option = fold(orElse) { Some(it) } public fun EagerEffect.toOption(orElse: (E) -> Option): Option = fold(orElse) { Some(it) } -/** Run the [Effect] by returning [Option] of [A], or [None] if shifted with [None]. */ +/** Run the [Effect] by returning [Option] of [A], or [None] if raised with [None]. */ public suspend fun Effect.toOption(): Option = option { invoke() } public fun EagerEffect.toOption(): Option = option { invoke() } /** Run the [Effect] by returning [Result] of [A], [orElse] run the fallback lambda and returning its result of [Result] of [A]. */ -public suspend fun Effect.toResult(orElse: suspend (E) -> Result): Result = fold({ orElse(it) }, { Result.success(it) }) -public fun EagerEffect.toResult(orElse: (E) -> Result): Result = fold({ orElse(it) }, { Result.success(it) }) +public suspend fun Effect.toResult(orElse: suspend (E) -> Result): Result = + fold({ orElse(it) }, { Result.success(it) }) +public fun EagerEffect.toResult(orElse: (E) -> Result): Result = + fold({ orElse(it) }, { Result.success(it) }) -/** Run the [Effect] by returning [Result] of [A], or [Result.Failure] if shifted with [Throwable]. */ +/** Run the [Effect] by returning [Result] of [A], or [Result.Failure] if raised with [Throwable]. */ public suspend fun Effect.toResult(): Result = result { invoke() } public fun EagerEffect.toResult(): Result = result { invoke() } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt index 268951b24d3..d4f1736edfd 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/Raise.kt @@ -175,7 +175,7 @@ public interface Raise { * }.also(::println) * * either { - * effect { raise("failed") }.recover { str -> shift(-1) } + * effect { raise("failed") }.recover { str -> raise(-1) } * }.also(::println) * } * ``` @@ -245,12 +245,12 @@ public inline fun Raise.catch( ): A = catch(action) { t: Throwable -> if (t is T) catch(t) else throw t } @EffectDSL -public inline fun Raise.ensure(condition: Boolean, shift: () -> R): Unit = - if (condition) Unit else raise(shift()) +public inline fun Raise.ensure(condition: Boolean, raise: () -> R): Unit = + if (condition) Unit else raise(raise()) @OptIn(ExperimentalContracts::class) @EffectDSL -public inline fun Raise.ensureNotNull(value: B?, shift: () -> R): B { +public inline fun Raise.ensureNotNull(value: B?, raise: () -> R): B { contract { returns() implies (value != null) } - return value ?: raise(shift()) + return value ?: raise(raise()) } diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/StateRaise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/StateRaise.kt new file mode 100644 index 00000000000..20f4791cc7e --- /dev/null +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/continuations/StateRaise.kt @@ -0,0 +1,39 @@ +@file:OptIn(ExperimentalTypeInference::class) + +package arrow.core.continuations + +import arrow.atomic.Atomic +import kotlin.experimental.ExperimentalTypeInference + +/** + * Intersection of Atomic & Raise. + * Will be replaced by `context(Atomic, Raise)` later + */ +public interface StateRaise : Atomic, Raise + +public typealias StateEffect = suspend StateRaise.() -> A + +public typealias EagerStateEffect = StateRaise.() -> A + +public inline fun fold( + initial: State, + @BuilderInference program: StateRaise.() -> A, + error: (state: State, error: Throwable) -> B, + recover: (state: State, raised: R) -> B, + transform: (state: State, value: A) -> B, +): B { + val state = Atomic(initial) + return fold( + { program(DefaultStateRaise(state, this)) }, + { error(state.value, it) }, + { recover(state.value, it) }, + { transform(state.value, it) } + ) +} + +/** Default intersection boilerplate. PublishedApi to support _inline_ */ +@PublishedApi +internal class DefaultStateRaise( + state: Atomic, + raise: Raise, +) : StateRaise, Atomic by state, Raise by raise {} diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/memoization.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/memoization.kt index 52d0e8d1101..89412b5eacb 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/memoization.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/memoization.kt @@ -2,8 +2,8 @@ package arrow.core -import arrow.core.continuations.AtomicRef -import arrow.core.continuations.loop +import arrow.atomic.Atomic +import arrow.atomic.loop import kotlin.jvm.JvmName /** @@ -113,9 +113,9 @@ private data class MemoizeKey5( } private class MemoizedHandler, out R>(val f: F) { - private val cache = AtomicRef(emptyMap()) + private val cache = Atomic(emptyMap()) operator fun invoke(k: K): R { - val cached = cache.get()[k] + val cached = cache.value[k] // No cached value found, compute one return if (cached == null) { val b = k(f) diff --git a/arrow-libs/core/arrow-core/src/jsMain/kotlin/arrow/core/continuations/AtomicRef.kt b/arrow-libs/core/arrow-core/src/jsMain/kotlin/arrow/core/continuations/AtomicRef.kt deleted file mode 100644 index d839476478d..00000000000 --- a/arrow-libs/core/arrow-core/src/jsMain/kotlin/arrow/core/continuations/AtomicRef.kt +++ /dev/null @@ -1,30 +0,0 @@ -package arrow.core.continuations - -public actual class AtomicRef actual constructor(initialValue: V) { - private var internalValue: V = initialValue - - /** - * Compare current value with expected and set to new if they're the same. Note, 'compare' is checking - * the actual object id, not 'equals'. - */ - public actual fun compareAndSet(expected: V, new: V): Boolean { - return if (expected === internalValue) { - internalValue = new - true - } else { - false - } - } - - public actual fun getAndSet(value: V): V { - val oldValue = internalValue - internalValue = value - return oldValue - } - - public actual fun get(): V = internalValue - - public actual fun set(value: V) { - internalValue = value - } -} diff --git a/arrow-libs/core/arrow-core/src/jvmMain/kotlin/arrow/core/continuations/AtomicRef.kt b/arrow-libs/core/arrow-core/src/jvmMain/kotlin/arrow/core/continuations/AtomicRef.kt deleted file mode 100644 index 564deb22311..00000000000 --- a/arrow-libs/core/arrow-core/src/jvmMain/kotlin/arrow/core/continuations/AtomicRef.kt +++ /dev/null @@ -1,7 +0,0 @@ -@file:JvmName("AtomicReferenceActual") - -package arrow.core.continuations - -import java.util.concurrent.atomic.AtomicReference - -public actual typealias AtomicRef = AtomicReference diff --git a/arrow-libs/core/arrow-core/src/nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt b/arrow-libs/core/arrow-core/src/nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt deleted file mode 100644 index bc63616ad47..00000000000 --- a/arrow-libs/core/arrow-core/src/nativeMain/kotlin/arrow/core/continuations/AtomicRef.kt +++ /dev/null @@ -1,30 +0,0 @@ -package arrow.core.continuations - -import kotlin.native.concurrent.AtomicReference -import kotlin.native.concurrent.freeze -import kotlin.native.concurrent.isFrozen - -public actual class AtomicRef actual constructor(initialValue: V) { - private val atom = AtomicReference(initialValue.freeze()) - public actual fun get(): V = atom.value - - public actual fun set(value: V) { - atom.value = value.freeze() - } - - public actual fun getAndSet(value: V): V { - if (atom.isFrozen) value.freeze() - while (true) { - val cur = atom.value - if (cur === value) return cur - if (atom.compareAndSwap(cur, value) === cur) return cur - } - } - - /** - * Compare current value with expected and set to new if they're the same. Note, 'compare' is checking - * the actual object id, not 'equals'. - */ - public actual fun compareAndSet(expected: V, new: V): Boolean = - atom.compareAndSet(expected, new.freeze()) -} diff --git a/arrow-libs/fx/arrow-fx-coroutines-test/src/jvmMain/kotlin/arrow/fx/coroutines/predef-test-jvm.kt b/arrow-libs/fx/arrow-fx-coroutines-test/src/jvmMain/kotlin/arrow/fx/coroutines/predef-test-jvm.kt index b05351a74f4..76c3924dfef 100644 --- a/arrow-libs/fx/arrow-fx-coroutines-test/src/jvmMain/kotlin/arrow/fx/coroutines/predef-test-jvm.kt +++ b/arrow-libs/fx/arrow-fx-coroutines-test/src/jvmMain/kotlin/arrow/fx/coroutines/predef-test-jvm.kt @@ -1,6 +1,6 @@ package arrow.fx.coroutines -import arrow.core.continuations.AtomicRef +import arrow.atomic.Atomic import arrow.core.test.concurrency.deprecateArrowTestModules import java.util.concurrent.ThreadFactory import kotlin.coroutines.CoroutineContext @@ -17,8 +17,8 @@ public val threadName: suspend () -> String = @Deprecated(deprecateArrowTestModules) public class NamedThreadFactory(private val mkName: (Int) -> String) : ThreadFactory { - private val count = AtomicRef(0) + private val count = Atomic(0) override fun newThread(r: Runnable): Thread = - Thread(r, mkName(count.get())) + Thread(r, mkName(count.value)) .apply { isDaemon = true } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api index be169607705..ee299e3bc75 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api +++ b/arrow-libs/fx/arrow-fx-coroutines/api/arrow-fx-coroutines.api @@ -2,32 +2,6 @@ public final class arrow/fx/coroutines/AcquireStep { public static final field INSTANCE Larrow/fx/coroutines/AcquireStep; } -public abstract interface class arrow/fx/coroutines/Atomic { - public static final field Companion Larrow/fx/coroutines/Atomic$Companion; - public abstract fun access (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun get (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun getAndSet (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun getAndUpdate (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun lens (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Larrow/fx/coroutines/Atomic; - public abstract fun modify (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun modifyGet (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun set (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun setAndGet (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun tryModify (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun tryUpdate (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun update (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun updateAndGet (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - -public final class arrow/fx/coroutines/Atomic$Companion { - public final fun invoke (Ljava/lang/Object;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun unsafe (Ljava/lang/Object;)Larrow/fx/coroutines/Atomic; -} - -public final class arrow/fx/coroutines/Atomic$DefaultImpls { - public static fun lens (Larrow/fx/coroutines/Atomic;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)Larrow/fx/coroutines/Atomic; -} - public final class arrow/fx/coroutines/BracketKt { public static final fun bracket (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun bracketCase (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -44,7 +18,7 @@ public final class arrow/fx/coroutines/BuildersKt { public final class arrow/fx/coroutines/CircuitBreaker { public static final field Companion Larrow/fx/coroutines/CircuitBreaker$Companion; - public synthetic fun (Ljava/util/concurrent/atomic/AtomicReference;IDDDLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Larrow/atomic/Atomic;IDDDLkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun awaitClose (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun doOnClosed (Lkotlin/jvm/functions/Function1;)Larrow/fx/coroutines/CircuitBreaker; public final fun doOnHalfOpen (Lkotlin/jvm/functions/Function1;)Larrow/fx/coroutines/CircuitBreaker; @@ -52,7 +26,7 @@ public final class arrow/fx/coroutines/CircuitBreaker { public final fun doOnRejectedTask (Lkotlin/jvm/functions/Function1;)Larrow/fx/coroutines/CircuitBreaker; public final fun protectEither (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public final fun protectOrThrow (Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public final fun state (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun state ()Larrow/fx/coroutines/CircuitBreaker$State; } public final class arrow/fx/coroutines/CircuitBreaker$Companion { diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Atomic.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Atomic.kt deleted file mode 100644 index e8b16b27798..00000000000 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Atomic.kt +++ /dev/null @@ -1,367 +0,0 @@ -package arrow.fx.coroutines - -import arrow.core.continuations.AtomicRef - -/** - * An [Atomic] with an initial value of [A]. - * - * [Atomic] wraps `atomic`, so that you can also use it on a top-level function or pass it around. - * In other languages this data type is also known as `Ref`, `IORef` or Concurrent safe Reference. - * So in case you don't need to pass around an atomic reference, or use it in top-level functions - * it's advised to use `atomic` from Atomic Fu directly. - * - * ```kotlin - * import arrow.fx.coroutines.* - * - * suspend fun main() { - * val count = Atomic(0) - * - * (0 until 20_000).parTraverse { - * count.update(Int::inc) - * } - * println(count.get()) - * } - * ``` - * - * - * [Atomic] also offers some other interesting operators such as [modify], [tryUpdate], [access] & [lens]. - */ -public interface Atomic { - - /** - * Obtains the current value. - * Since [AtomicRef] is always guaranteed to have a value, the returned action completes immediately after being bound. - */ - public suspend fun get(): A - - /** - * Sets the current value to [a]. - * The returned action completes after the reference has been successfully set. - */ - public suspend fun set(a: A): Unit - - /** - * Replaces the current value with [a], returning the *old* value. - */ - public suspend fun getAndSet(a: A): A - - /** - * Replaces the current value with [a], returning the *new* value. - */ - public suspend fun setAndGet(a: A): A - - /** - * Updates the current value using the supplied function [f]. - * - * If another modification occurs between the time the current value is read and subsequently updated, - * the modification is retried using the new value. Hence, [f] may be invoked multiple times. - */ - public suspend fun update(f: (A) -> A): Unit - - /** - * Modifies the current value using the supplied update function and returns the *old* value. - * - * @see [update], [f] may be invoked multiple times. - */ - public suspend fun getAndUpdate(f: (A) -> A): A - - /** - * Modifies the current value using the supplied update function and returns the *new* value. - * - * @see [update], [f] may be invoked multiple times. - */ - public suspend fun updateAndGet(f: (A) -> A): A - - /** - * Modify allows to inspect the state [A] of the [AtomicRef], update it and extract a different state [B]. - * - * ```kotlin - * import arrow.fx.coroutines.* - * - * typealias Id = Int - * data class Job(val description: String) - * - * val initialState = (0 until 10).map { i -> Pair(i, Job("Task #$i")) } - * - * suspend fun main(): Unit { - * val jobs = Atomic(initialState) - * - * val batch = jobs.modify { j -> - * val batch = j.take(5) - * Pair(j.drop(5), batch) - * } - * - * batch.forEach { (id, job) -> - * println("Going to work on $job with id $id\n") - * } - * - * println("Remaining: ${jobs.get()}") - * } - * ``` - * - */ - public suspend fun modify(f: (A) -> Pair): B - - /** - * ModifyGet allows to inspect state [A], update it and extract a different state [B]. - * In contrast to [modify], it returns a [Pair] of the updated state [A] and the extracted state [B]. - * - * @see [modify] for an example - */ - public suspend fun modifyGet(f: (A) -> Pair): Pair - - /** - * Attempts to modify the current value once, in contrast to [update] which calls [f] until it succeeds. - * - * @returns `false` if concurrent modification completes between the time the variable is read and the time it is set. - */ - public suspend fun tryUpdate(f: (A) -> A): Boolean - - /** - * Attempts to inspect the state, uptade it, and extract a different state. - * - * [tryModify] behaves as [tryUpdate] but allows the update function to return an output value of type [B]. - * - * @returns `null` if the update fails and [B] otherwise. - */ - public suspend fun tryModify(f: (A) -> Pair): B? - - /** - * Obtains a snapshot of the current value, and a setter for updating it. - * - * This is useful when you need to execute effects with the original result while still ensuring an atomic update. - * - * The setter will return `false` if another concurrent call invalidated the snapshot (modified the value). - * It will return `true` if setting the value was successful. - * - * Once it has returned `false` or been used once, a setter never succeeds again. - */ - public suspend fun access(): Pair Boolean> - - /** - * Creates an [AtomicRef] for [B] based on provided a [get] and [set] operation. - * - * This is useful when you have an [AtomicRef] of a `data class` - * and need to work with with certain properties individually, - * or want to hide parts of your domain from a dependency. - * - * ```kotlin - * import arrow.fx.coroutines.* - * - * data class Preference(val isEnabled: Boolean) - * data class User(val name: String, val age: Int, val preference: Preference) - * data class ViewState(val user: User) - * - * suspend fun main(): Unit { - * //sampleStart - * val state: Atomic = Atomic(ViewState(User("Simon", 27, Preference(false)))) - * val isEnabled: Atomic = - * state.lens( - * { it.user.preference.isEnabled }, - * { state, isEnabled -> - * state.copy( - * user = - * state.user.copy( - * preference = - * state.user.preference.copy(isEnabled = isEnabled) - * ) - * ) - * } - * ) - * isEnabled.set(true) - * println(state.get()) - * } - * ``` - * - */ - public fun lens(get: (A) -> B, set: (A, B) -> A): arrow.fx.coroutines.Atomic = - LensAtomic(this, get, set) - - public companion object { - - /** - * Creates an [AtomicRef] with an initial value of [A]. - * - * Data type on top of [atomic] to use in parallel functions. - * - * ```kotlin - * import arrow.fx.coroutines.* - * - * suspend fun main() { - * val count = Atomic(0) - * (0 until 20_000).parTraverse { - * count.update(Int::inc) - * } - * println(count.get()) - * } - * ``` - * - */ - public suspend operator fun invoke(a: A): arrow.fx.coroutines.Atomic = unsafe(a) - public fun unsafe(a: A): arrow.fx.coroutines.Atomic = DefaultAtomic(a) - } -} - -private class DefaultAtomic(a: A) : arrow.fx.coroutines.Atomic { - - private val ar = AtomicRef(a) - - public override suspend fun get(): A = - ar.get() - - public override suspend fun set(a: A): Unit { - ar.set(a) - } - - public override suspend fun getAndSet(a: A): A = - ar.getAndSet(a) - - public override suspend fun setAndGet(a: A): A { - ar.set(a) - return a - } - - public override suspend fun getAndUpdate(f: (A) -> A): A { - while (true) { - val cur = get() - val upd = f(cur) - if (ar.compareAndSet(cur, upd)) return cur - } - } - - public override suspend fun updateAndGet(f: (A) -> A): A { - while (true) { - val cur = ar.get() - val upd = f(cur) - if (ar.compareAndSet(cur, upd)) return upd - } - } - - public override suspend fun access(): Pair Boolean> { - val snapshot = ar.get() - val hasBeenCalled = AtomicRef(false) - val setter: suspend (A) -> Boolean = { a: A -> - hasBeenCalled.compareAndSet(false, true) && ar.compareAndSet(snapshot, a) - } - - return Pair(snapshot, setter) - } - - public override suspend fun tryUpdate(f: (A) -> A): Boolean = - tryModify { a -> Pair(f(a), Unit) } != null - - public override suspend fun tryModify(f: (A) -> Pair): B? { - val a = ar.get() - val (u, b) = f(a) - return if (ar.compareAndSet(a, u)) b - else null - } - - public override suspend fun update(f: (A) -> A): Unit = - modify { a -> Pair(f(a), Unit) } - - public override suspend fun modify(f: (A) -> Pair): B { - tailrec fun go(): B { - val a = ar.get() - val (u, b) = f(a) - return if (!ar.compareAndSet(a, u)) go() else b - } - - return go() - } - - public override suspend fun modifyGet(f: (A) -> Pair): Pair { - tailrec fun go(): Pair { - val a = ar.get() - val res = f(a) - return if (!ar.compareAndSet(a, res.first)) go() else res - } - - return go() - } -} - -private class LensAtomic( - private val underlying: arrow.fx.coroutines.Atomic, - private val lensGet: (A) -> B, - private val lensSet: (A, B) -> A -) : arrow.fx.coroutines.Atomic { - - public override suspend fun setAndGet(a: B): B = - underlying.modify { old -> - Pair(lensModify(old) { a }, a) - } - - public override suspend fun getAndUpdate(f: (B) -> B): B = - underlying.modify { old -> - Pair(lensModify(old, f), lensGet(old)) - } - - public override suspend fun updateAndGet(f: (B) -> B): B = - underlying.modify { old -> - val new = lensModify(old, f) - Pair(new, lensGet(new)) - } - - public override suspend fun get(): B = - lensGet(underlying.get()) - - public override suspend fun set(a: B) { - underlying.update { old -> lensModify(old) { a } } - } - - public override suspend fun getAndSet(a: B): B = - underlying.modify { old -> - Pair(lensModify(old) { a }, lensGet(old)) - } - - public override suspend fun update(f: (B) -> B) = - underlying.update { old -> lensModify(old, f) } - - public override suspend fun modify(f: (B) -> Pair): C = - underlying.modify { old -> - val oldB = lensGet(old) - val (b, c) = f(oldB) - Pair(lensSet(old, b), c) - } - - public override suspend fun modifyGet(f: (B) -> Pair): Pair = - underlying.modifyGet { old -> - val oldB = lensGet(old) - val (b, c) = f(oldB) - Pair(lensSet(old, b), c) - }.let { (a, c) -> Pair(lensGet(a), c) } - - public override suspend fun tryUpdate(f: (B) -> B): Boolean = - tryModify { b -> Pair(f(b), Unit) } != null - - public override suspend fun tryModify(f: (B) -> Pair): C? = - underlying.tryModify { a -> - val oldB = lensGet(a) - val (b, result) = f(oldB) - Pair(lensSet(a, b), result) - } - - public override suspend fun access(): Pair Boolean> { - val snapshotA = underlying.get() - val snapshotB = lensGet(snapshotA) - - val setter: suspend (B) -> Boolean = { b: B -> - val hasBeenCalled = AtomicRef(false) - - suspend { - val called = hasBeenCalled.compareAndSet(false, true) - - underlying.tryModify { a -> - if (called && lensGet(a) == snapshotA) Pair(lensSet(a, b), true) - else Pair(a, false) - } ?: false - }.invoke() - } - - return Pair(snapshotB, setter) - } - - private fun lensModify(s: A, f: (B) -> B): A = - lensSet(s, f(lensGet(s))) -} diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/CircuitBreaker.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/CircuitBreaker.kt index 81c991cde98..cbefde26406 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/CircuitBreaker.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/CircuitBreaker.kt @@ -1,7 +1,7 @@ package arrow.fx.coroutines import arrow.core.Either -import arrow.core.continuations.AtomicRef +import arrow.atomic.Atomic import arrow.core.identity import arrow.fx.coroutines.CircuitBreaker.State.Closed import arrow.fx.coroutines.CircuitBreaker.State.HalfOpen @@ -128,7 +128,7 @@ import kotlin.time.DurationUnit */ public class CircuitBreaker private constructor( - private val state: AtomicRef, + private val state: Atomic, private val maxFailures: Int, private val resetTimeoutNanos: Double, private val exponentialBackoffFactor: Double, @@ -144,7 +144,7 @@ private constructor( /** Returns the current [CircuitBreaker.State], meant for debugging purposes. */ - public suspend fun state(): State = state.get() + public fun state(): State = state.value /** * Awaits for this `CircuitBreaker` to be [CircuitBreaker.State.Closed]. @@ -155,10 +155,10 @@ private constructor( * state again. */ public suspend fun awaitClose(): Unit = - when (val curr = state.get()) { + when (val curr = state.value) { is Closed -> Unit is Open -> curr.awaitClose.await() - is State.HalfOpen -> curr.awaitClose.await() + is HalfOpen -> curr.awaitClose.await() } /** @@ -179,7 +179,7 @@ private constructor( * If an exception in [fa] occurs it will be rethrown */ public tailrec suspend fun protectOrThrow(fa: suspend () -> A): A = - when (val curr = state.get()) { + when (val curr = state.value) { is Closed -> { val attempt = try { Either.Right(fa.invoke()) @@ -209,7 +209,7 @@ private constructor( ) } } - is State.HalfOpen -> { + is HalfOpen -> { // CircuitBreaker is in HalfOpen state, which means we still reject all // tasks, while waiting to see if our reset attempt succeeds or fails onRejected.invoke() @@ -221,7 +221,7 @@ private constructor( * triggering the `Open` state if necessary. */ private tailrec suspend fun markOrResetFailures(result: Either): A = - when (val curr = state.get()) { + when (val curr = state.value) { is Closed -> { when (result) { is Either.Right -> { @@ -284,13 +284,13 @@ private constructor( is ExitCase.Cancelled -> { // We need to return to Open state // otherwise we get stuck in Half-Open (see https://github.com/monix/monix/issues/1080 ) - state.set(Open(lastStartedAt, resetTimeout, awaitClose)) + state.value = Open(lastStartedAt, resetTimeout, awaitClose) onOpen.invoke() } ExitCase.Completed -> { // While in HalfOpen only a reset attempt is allowed to update // the state, so setting this directly is safe - state.set(Closed(0)) + state.value = Closed(0) awaitClose.complete(Unit) onClosed.invoke() } @@ -301,7 +301,7 @@ private constructor( if (maxResetTimeout.isFinite() && value > maxResetTimeout) maxResetTimeout else value val ts = timeInMillis() - state.set(Open(ts, nextTimeout, awaitClose)) + state.value = Open(ts, nextTimeout, awaitClose) onOpen.invoke() } } @@ -623,7 +623,7 @@ private constructor( onOpen: suspend () -> Unit = { }, ): CircuitBreaker = CircuitBreaker( - state = AtomicRef(Closed(0)), + state = Atomic(Closed(0)), maxFailures = maxFailures .takeIf { it >= 0 } .let { requireNotNull(it) { "maxFailures expected to be greater than or equal to 0, but was $maxFailures" } }, diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt index 399c6a76797..4b4dd18d038 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Resource.kt @@ -1,7 +1,7 @@ package arrow.fx.coroutines -import arrow.core.continuations.AtomicRef -import arrow.core.continuations.update +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.identity import arrow.core.prependTo import kotlinx.coroutines.CancellationException @@ -491,7 +491,7 @@ public suspend fun Resource.allocated(): Pair Un @JvmInline private value class ResourceScopeImpl( - private val finalizers: AtomicRef Unit>> = AtomicRef(emptyList()), + private val finalizers: Atomic Unit>> = Atomic(emptyList()), ) : ResourceScope { override suspend fun Resource.bind(): A = invoke(this@ResourceScopeImpl) @@ -514,7 +514,7 @@ private value class ResourceScopeImpl( suspend fun cancelAll( exitCase: ExitCase, first: Throwable? = null, - ): Throwable? = finalizers.get().fold(first) { acc, finalizer -> + ): Throwable? = finalizers.value.fold(first) { acc, finalizer -> val other = kotlin.runCatching { finalizer(exitCase) }.exceptionOrNull() other?.let { acc?.apply { addSuppressed(other) } ?: other diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/AtomicTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/AtomicTest.kt deleted file mode 100644 index 19c36f48dd1..00000000000 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/AtomicTest.kt +++ /dev/null @@ -1,127 +0,0 @@ -package arrow.fx.coroutines - -import io.kotest.matchers.shouldBe -import io.kotest.property.Arb -import io.kotest.property.arbitrary.int -import kotlin.coroutines.Continuation -import kotlin.coroutines.EmptyCoroutineContext -import kotlin.coroutines.intrinsics.startCoroutineUninterceptedOrReturn - -class AtomicTest : ArrowFxSpec( - spec = { - - "set get - successful" { - checkAll(Arb.int(), Arb.int()) { x, y -> - val r = Atomic(x) - r.set(y) - r.get() shouldBe y - } - } - - "getAndSet - successful" { - checkAll(Arb.int(), Arb.int()) { x, y -> - val ref = Atomic(x) - ref.getAndSet(y) shouldBe x - ref.get() shouldBe y - } - } - - "access - successful" { - checkAll(Arb.int(), Arb.int()) { x, y -> - val ref = Atomic(x) - val (_, setter) = ref.access() - setter(y) shouldBe true - ref.get() shouldBe y - } - } - - "access - setter should fail if value is modified before setter is called" { - checkAll(Arb.int(), Arb.int()) { x, z -> - val ref = Atomic(x) - val (_, setter) = ref.access() - ref.update { it + 1 } - setter(z) shouldBe false - ref.get() shouldBe x + 1 - } - } - - "access - setter should fail if called twice" { - checkAll(Arb.int(), Arb.int(), Arb.int(), Arb.int()) { w, x, y, z -> - val ref = Atomic(w) - val (_, setter) = ref.access() - setter(x) shouldBe true - ref.set(y) - setter(z) shouldBe false - ref.get() shouldBe y - } - } - - "tryUpdate - modification occurs successfully" { - checkAll(Arb.int()) { x -> - val ref = Atomic(x) - ref.tryUpdate { it + 1 } - ref.get() shouldBe x + 1 - } - } - - "tryUpdate - should fail to update if modification has occurred" { - checkAll(Arb.int()) { x -> - val ref = Atomic(x) - ref.tryUpdate { - suspend { ref.update(Int::inc) } - .startCoroutineUninterceptedOrReturn(Continuation(EmptyCoroutineContext) { }) - it + 1 - } shouldBe false - } - } - - "consistent set update" { - checkAll(Arb.int(), Arb.int()) { x, y -> - val set = suspend { - val r = Atomic(x) - r.set(y) - r.get() - } - - val update = suspend { - val r = Atomic(x) - r.update { y } - r.get() - } - - set() shouldBe update() - } - } - - "access id" { - checkAll(Arb.int()) { x -> - val r = Atomic(x) - val (a, _) = r.access() - r.get() shouldBe a - } - } - - "consistent access tryUpdate" { - checkAll(Arb.int()) { x -> - val acccessMap = suspend { - val r = Atomic(x) - val (a, setter) = r.access() - setter(a + 1) - } - val tryUpdate = suspend { - val r = Atomic(x) - r.tryUpdate { it + 1 } - } - - acccessMap() shouldBe tryUpdate() - } - } - - "concurrent modifications" { - val finalValue = 50_000 - val r = Atomic(0) - (0 until finalValue).parTraverse { r.update { it + 1 } } - r.get() shouldBe finalValue - } - } -) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseEitherTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseEitherTest.kt index 7cdd933e762..f6c67ed496f 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseEitherTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseEitherTest.kt @@ -1,5 +1,7 @@ package arrow.fx.coroutines +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.left import arrow.core.right @@ -18,7 +20,7 @@ class ParTraverseEitherTest : ArrowFxSpec( (0 until 100).parTraverseEither { ref.update { it + 1 }.right() } - ref.get() shouldBe 100 + ref.value shouldBe 100 } "parTraverseEither runs in parallel" { diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseResultTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseResultTest.kt index 32c4a0aade7..6bab1da436c 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseResultTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseResultTest.kt @@ -1,5 +1,7 @@ package arrow.fx.coroutines +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.sequence import arrow.core.test.generators.result @@ -19,7 +21,7 @@ class ParTraverseResultTest : ArrowFxSpec( (0 until 100).parTraverseResult { Result.success(ref.update { it + 1 }) } - ref.get() shouldBe 100 + ref.value shouldBe 100 } "parTraverseResult runs in parallel" { diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseTest.kt index 2b8ea17480d..9cb6b47207e 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseTest.kt @@ -1,5 +1,7 @@ package arrow.fx.coroutines +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import io.kotest.matchers.should import io.kotest.matchers.shouldBe @@ -20,7 +22,7 @@ class ParTraverseTest : ArrowFxSpec( (0 until 100).parTraverse { ref.update { it + 1 } } - ref.get() shouldBe 100 + ref.value shouldBe 100 } "parTraverse runs in parallel" { @@ -75,7 +77,7 @@ class ParTraverseTest : ArrowFxSpec( (0 until 100).parTraverseN(5) { ref.update { it + 1 } } - ref.get() shouldBe 100 + ref.value shouldBe 100 } "parTraverseN(3) runs in (3) parallel" { @@ -154,7 +156,7 @@ class ParTraverseTest : ArrowFxSpec( .map { suspend { ref.update { it + 1 } } } .parSequenceN(5) - ref.get() shouldBe 100 + ref.value shouldBe 100 } } ) diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseValidatedTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseValidatedTest.kt index 26be83f0a61..133e1ffa30d 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseValidatedTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ParTraverseValidatedTest.kt @@ -1,5 +1,7 @@ package arrow.fx.coroutines +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.NonEmptyList import arrow.core.Validated @@ -21,7 +23,7 @@ class ParTraverseValidatedTest : ArrowFxSpec( (0 until 100).parTraverseValidated(Semigroup.nonEmptyList()) { ref.update { it + 1 }.validNel() } - ref.get() shouldBe 100 + ref.value shouldBe 100 } "parTraverseValidated runs in parallel" { diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ScheduleTest.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ScheduleTest.kt index 4ae2907d34c..6cf8689212d 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ScheduleTest.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/ScheduleTest.kt @@ -1,5 +1,7 @@ package arrow.fx.coroutines +import arrow.atomic.Atomic +import arrow.atomic.updateAndGet import arrow.core.Either import arrow.core.Eval import io.kotest.assertions.fail @@ -255,7 +257,7 @@ class ScheduleTest : ArrowFxSpec( } l should leftException(exception) - count.get() shouldBe 20_001 + count.value shouldBe 20_001 } "retry succeeds if no exception is thrown" { diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap2Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap2Test.kt index 9f8ef59c261..92b980fffa5 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap2Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap2Test.kt @@ -1,8 +1,9 @@ package arrow.fx.coroutines.parMapN +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.fx.coroutines.ArrowFxSpec -import arrow.fx.coroutines.Atomic import arrow.fx.coroutines.ExitCase import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.guaranteeCase @@ -40,14 +41,14 @@ class ParMap2Test : ArrowFxSpec( r.update { i -> "$i$a" } }, { - r.set("$b") + r.value = "$b" modifyGate.complete(0) } ) { _a, _b -> Pair(_a, _b) } - r.get() shouldBe "$b$a" + r.value shouldBe "$b$a" } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap3Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap3Test.kt index cd2fce7349a..5afbf9d040f 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap3Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap3Test.kt @@ -1,8 +1,9 @@ package arrow.fx.coroutines.parMapN +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.fx.coroutines.ArrowFxSpec -import arrow.fx.coroutines.Atomic import arrow.fx.coroutines.ExitCase import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.leftException @@ -40,14 +41,14 @@ class ParMap3Test : ArrowFxSpec( modifyGate2.complete(Unit) }, { - r.set("$c") + r.value ="$c" modifyGate1.complete(Unit) } ) { _a, _b, _c -> Triple(_a, _b, _c) } - r.get() shouldBe "$c$b$a" + r.value shouldBe "$c$b$a" } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap4Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap4Test.kt index b3cbc921b31..103b68d6ec8 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap4Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap4Test.kt @@ -1,9 +1,10 @@ package arrow.fx.coroutines.parMapN +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.Tuple4 import arrow.fx.coroutines.ArrowFxSpec -import arrow.fx.coroutines.Atomic import arrow.fx.coroutines.ExitCase import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.leftException @@ -47,14 +48,14 @@ class ParMap4Test : ArrowFxSpec( modifyGate3.complete(Unit) }, { - r.set("$d") + r.value = "$d" modifyGate1.complete(Unit) } ) { _a, _b, _c, _d -> Tuple4(_a, _b, _c, _d) } - r.get() shouldBe "$d$c$b$a" + r.value shouldBe "$d$c$b$a" } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap5Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap5Test.kt index 6ceeb1a7519..cbcc393acde 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap5Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap5Test.kt @@ -1,9 +1,10 @@ package arrow.fx.coroutines.parMapN +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.Tuple5 import arrow.fx.coroutines.ArrowFxSpec -import arrow.fx.coroutines.Atomic import arrow.fx.coroutines.ExitCase import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.leftException @@ -53,14 +54,14 @@ class ParMap5Test : ArrowFxSpec( modifyGate4.complete(Unit) }, { - r.set("$e") + r.value = "$e" modifyGate1.complete(Unit) } ) { _a, _b, _c, _d, _e -> Tuple5(_a, _b, _c, _d, _e) } - r.get() shouldBe "$e$d$c$b$a" + r.value shouldBe "$e$d$c$b$a" } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap6Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap6Test.kt index 78982a64066..2f474145a46 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap6Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap6Test.kt @@ -1,9 +1,10 @@ package arrow.fx.coroutines.parMapN +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.Tuple6 import arrow.fx.coroutines.ArrowFxSpec -import arrow.fx.coroutines.Atomic import arrow.fx.coroutines.ExitCase import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.leftException @@ -59,14 +60,14 @@ class ParMap6Test : ArrowFxSpec( modifyGate5.complete(Unit) }, { - r.set("$f") + r.value = "$f" modifyGate1.complete(Unit) } ) { _a, _b, _c, _d, _e, _f -> Tuple6(_a, _b, _c, _d, _e, _f) } - r.get() shouldBe "$f$e$d$c$b$a" + r.value shouldBe "$f$e$d$c$b$a" } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap7Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap7Test.kt index 9d77f712eed..a14f36ea24a 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap7Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap7Test.kt @@ -1,9 +1,10 @@ package arrow.fx.coroutines.parMapN +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.Tuple7 import arrow.fx.coroutines.ArrowFxSpec -import arrow.fx.coroutines.Atomic import arrow.fx.coroutines.ExitCase import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.leftException @@ -65,14 +66,14 @@ class ParMap7Test : ArrowFxSpec( modifyGate6.complete(Unit) }, { - r.set("$g") + r.value = "$g" modifyGate1.complete(Unit) } ) { _a, _b, _c, _d, _e, _f, _g -> Tuple7(_a, _b, _c, _d, _e, _f, _g) } - r.get() shouldBe "$g$f$e$d$c$b$a" + r.value shouldBe "$g$f$e$d$c$b$a" } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap8Test.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap8Test.kt index 471acc0990c..6452c9c850c 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap8Test.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonTest/kotlin/arrow/fx/coroutines/parMapN/ParMap8Test.kt @@ -1,9 +1,10 @@ package arrow.fx.coroutines.parMapN +import arrow.atomic.Atomic +import arrow.atomic.update import arrow.core.Either import arrow.core.Tuple8 import arrow.fx.coroutines.ArrowFxSpec -import arrow.fx.coroutines.Atomic import arrow.fx.coroutines.ExitCase import arrow.fx.coroutines.awaitExitCase import arrow.fx.coroutines.leftException @@ -71,14 +72,14 @@ class ParMap8Test : ArrowFxSpec( modifyGate7.complete(Unit) }, { - r.set("$h") + r.value = "$h" modifyGate1.complete(Unit) } ) { _a, _b, _c, _d, _e, _f, _g, _h -> Tuple8(_a, _b, _c, _d, _e, _f, _g, _h) } - r.get() shouldBe "$h$g$f$e$d$c$b$a" + r.value shouldBe "$h$g$f$e$d$c$b$a" } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-02.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-02.kt deleted file mode 100644 index 4c1c9f2cbde..00000000000 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-02.kt +++ /dev/null @@ -1,24 +0,0 @@ -// This file was automatically generated from Atomic.kt by Knit tool. Do not edit. -package arrow.fx.coroutines.examples.exampleAtomic02 - -import arrow.fx.coroutines.* - -typealias Id = Int -data class Job(val description: String) - -val initialState = (0 until 10).map { i -> Pair(i, Job("Task #$i")) } - -suspend fun main(): Unit { - val jobs = Atomic(initialState) - - val batch = jobs.modify { j -> - val batch = j.take(5) - Pair(j.drop(5), batch) - } - - batch.forEach { (id, job) -> - println("Going to work on $job with id $id\n") - } - - println("Remaining: ${jobs.get()}") -} diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-03.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-03.kt deleted file mode 100644 index 4431588c459..00000000000 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-03.kt +++ /dev/null @@ -1,28 +0,0 @@ -// This file was automatically generated from Atomic.kt by Knit tool. Do not edit. -package arrow.fx.coroutines.examples.exampleAtomic03 - -import arrow.fx.coroutines.* - -data class Preference(val isEnabled: Boolean) -data class User(val name: String, val age: Int, val preference: Preference) -data class ViewState(val user: User) - -suspend fun main(): Unit { - //sampleStart - val state: Atomic = Atomic(ViewState(User("Simon", 27, Preference(false)))) - val isEnabled: Atomic = - state.lens( - { it.user.preference.isEnabled }, - { state, isEnabled -> - state.copy( - user = - state.user.copy( - preference = - state.user.preference.copy(isEnabled = isEnabled) - ) - ) - } - ) - isEnabled.set(true) - println(state.get()) -} diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-04.kt b/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-04.kt deleted file mode 100644 index ea357dcf2df..00000000000 --- a/arrow-libs/fx/arrow-fx-coroutines/src/jvmTest/kotlin/examples/example-atomic-04.kt +++ /dev/null @@ -1,12 +0,0 @@ -// This file was automatically generated from Atomic.kt by Knit tool. Do not edit. -package arrow.fx.coroutines.examples.exampleAtomic04 - -import arrow.fx.coroutines.* - -suspend fun main() { - val count = Atomic(0) - (0 until 20_000).parTraverse { - count.update(Int::inc) - } - println(count.get()) -} diff --git a/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/TVar.kt b/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/TVar.kt index 01224173376..ca32614ec5e 100644 --- a/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/TVar.kt +++ b/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/TVar.kt @@ -1,8 +1,8 @@ package arrow.fx.stm -import arrow.core.continuations.AtomicRef -import arrow.core.continuations.update -import arrow.core.continuations.updateAndGet +import arrow.atomic.Atomic +import arrow.atomic.update +import arrow.atomic.updateAndGet import arrow.fx.stm.internal.STMFrame import arrow.fx.stm.internal.STMTransaction import kotlin.coroutines.resume @@ -128,10 +128,10 @@ public class TVar internal constructor(a: A) { * This is used to implement locking. Reading threads have to loop until the value is released by a * transaction. */ - private val ref = AtomicRef(a as Any?) + private val ref = Atomic(a as Any?) internal val value - get() = ref.get() + get() = ref.value /** * Each TVar has a unique id which is used to get a total ordering of variables to ensure that locks @@ -147,7 +147,7 @@ public class TVar internal constructor(a: A) { * Changes are pushed to waiting transactions via [notify] */ // TODO Use a set here, and preferably something that uses sharing to avoid gc pressure from copying... - private val waiting = AtomicRef>>(emptyList()) + private val waiting = Atomic>>(emptyList()) override fun hashCode(): Int = id.hashCode() @@ -167,7 +167,7 @@ public class TVar internal constructor(a: A) { */ internal fun readI(): A { while (true) { - ref.get().let { + ref.value.let { if (it !is STMFrame) return@readI it as A } } @@ -251,4 +251,4 @@ public class TVar internal constructor(a: A) { } } -internal val globalC: AtomicRef = AtomicRef(0L) +internal val globalC: Atomic = Atomic(0L) diff --git a/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/internal/Impl.kt b/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/internal/Impl.kt index 327d6a68173..544890c9c7d 100644 --- a/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/internal/Impl.kt +++ b/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/internal/Impl.kt @@ -1,6 +1,6 @@ package arrow.fx.stm.internal -import arrow.core.continuations.AtomicRef +import arrow.atomic.Atomic import arrow.fx.stm.STM import arrow.fx.stm.TVar import kotlinx.coroutines.suspendCancellableCoroutine @@ -163,7 +163,7 @@ public expect object RetryException : Throwable * Keeps the continuation that [TVar]'s use to resume this transaction. */ internal class STMTransaction(val f: STM.() -> A) { - private val cont = AtomicRef?>(null) + private val cont = Atomic?>(null) /** * Any one resumptions is enough, because we enqueue on all read variables this might be called multiple times. @@ -189,7 +189,7 @@ internal class STMTransaction(val f: STM.() -> A) { val registered = mutableListOf>() suspendCancellableCoroutine susp@{ k -> - cont.set(k) + cont.value = k frame.accessMap .forEach { (tv, entry) -> diff --git a/settings.gradle.kts b/settings.gradle.kts index 3836a82132c..51c02774a74 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,6 +31,9 @@ project(":arrow-annotations").projectDir = file("arrow-libs/core/arrow-annotatio include("arrow-core") project(":arrow-core").projectDir = file("arrow-libs/core/arrow-core") +include("arrow-atomic") +project(":arrow-atomic").projectDir = file("arrow-libs/core/arrow-atomic") + val enableCompatibilityMetadataVariant = providers.gradleProperty("kotlin.mpp.enableCompatibilityMetadataVariant") .forUseAtConfigurationTime().orNull?.toBoolean() == true