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 index 4ba1cbca1cb..a69e3e9e534 100644 --- 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 @@ -1,5 +1,11 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.atomic +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + /** * [Atomic] value of [V]. * @@ -44,31 +50,55 @@ public var Atomic.value: T /** * 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 inline fun Atomic.loop(action: (V) -> Unit): Nothing { + contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) } + do { action(value) } while(true) +} -public inline fun Atomic.tryUpdate(function: (V) -> V): Boolean = tryUpdate(function) { _, _ -> } +public inline fun Atomic.tryUpdate(function: (V) -> V): Boolean { + contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) } + return tryUpdate(function) { _, _ -> } +} -public inline fun Atomic.update(function: (V) -> V): Unit = update(function) { _, _ -> } +public inline fun Atomic.update(function: (V) -> V) { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + update(function) { _, _ -> } +} /** * Updates variable atomically using the specified [function] of its value and returns its old value. */ -public inline fun Atomic.getAndUpdate(function: (V) -> V): V = update(function) { old, _ -> old } +public inline fun Atomic.getAndUpdate(function: (V) -> V): V { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { old, _ -> old } +} /** * Updates variable atomically using the specified [function] of its value and returns its new value. */ -public inline fun Atomic.updateAndGet(function: (V) -> V): V = update(function) { _, new -> new } +public inline fun Atomic.updateAndGet(function: (V) -> V): V { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { _, new -> new } +} @PublishedApi internal inline fun Atomic.update(function: (V) -> U, transform: (old: V, new: U) -> R): R { - while (true) { - tryUpdate(function) { old, new -> return transform(old, new) } + contract { + callsInPlace(function, InvocationKind.AT_LEAST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + loop { cur -> + val upd = function(value) + if(compareAndSet(cur, upd)) return transform(cur, upd) } } @PublishedApi internal inline fun Atomic.tryUpdate(function: (V) -> U, onUpdated: (old: V, new: U) -> Unit): Boolean { + contract { + callsInPlace(function, InvocationKind.EXACTLY_ONCE) + callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE) + } val cur = value val upd = function(cur) return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) } diff --git a/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicBoolean.kt b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicBoolean.kt index f84476f4848..daec04d8502 100644 --- a/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicBoolean.kt +++ b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicBoolean.kt @@ -1,5 +1,11 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.atomic +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + public class AtomicBoolean(value: Boolean) { private val inner = AtomicInt(value.toInt()) @@ -28,31 +34,55 @@ public class AtomicBoolean(value: Boolean) { /** * Infinite loop that reads this atomic variable and performs the specified [action] on its value. */ -public inline fun AtomicBoolean.loop(action: (Boolean) -> Unit): Nothing { while(true) { action(value) } } +public inline fun AtomicBoolean.loop(action: (Boolean) -> Unit): Nothing { + contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) } + do { action(value) } while(true) +} -public inline fun AtomicBoolean.tryUpdate(function: (Boolean) -> Boolean): Boolean = tryUpdate(function) { _, _ -> } +public inline fun AtomicBoolean.tryUpdate(function: (Boolean) -> Boolean): Boolean { + contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) } + return tryUpdate(function) { _, _ -> } +} -public inline fun AtomicBoolean.update(function: (Boolean) -> Boolean): Unit = update(function) { _, _ -> } +public inline fun AtomicBoolean.update(function: (Boolean) -> Boolean) { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + update(function) { _, _ -> } +} /** * Updates variable atomically using the specified [function] of its value and returns its old value. */ -public inline fun AtomicBoolean.getAndUpdate(function: (Boolean) -> Boolean): Boolean = update(function) { old, _ -> old } +public inline fun AtomicBoolean.getAndUpdate(function: (Boolean) -> Boolean): Boolean { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { old, _ -> old } +} /** * Updates variable atomically using the specified [function] of its value and returns its new value. */ -public inline fun AtomicBoolean.updateAndGet(function: (Boolean) -> Boolean): Boolean = update(function) { _, new -> new } +public inline fun AtomicBoolean.updateAndGet(function: (Boolean) -> Boolean): Boolean { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { _, new -> new } +} @PublishedApi internal inline fun AtomicBoolean.update(function: (Boolean) -> Boolean, transform: (old: Boolean, new: Boolean) -> R): R { - while (true) { - tryUpdate(function) { old, new -> return transform(old, new) } + contract { + callsInPlace(function, InvocationKind.AT_LEAST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + loop { cur -> + val upd = function(value) + if(compareAndSet(cur, upd)) return transform(cur, upd) } } @PublishedApi internal inline fun AtomicBoolean.tryUpdate(function: (Boolean) -> Boolean, onUpdated: (old: Boolean, new: Boolean) -> Unit): Boolean { + contract { + callsInPlace(function, InvocationKind.EXACTLY_ONCE) + callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE) + } val cur = value val upd = function(cur) return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) } diff --git a/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicInt.kt b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicInt.kt index e9e2260a283..069a4288279 100644 --- a/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicInt.kt +++ b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicInt.kt @@ -1,5 +1,11 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.atomic +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + public expect class AtomicInt(initialValue: Int) { public fun get(): Int @@ -23,31 +29,55 @@ public var AtomicInt.value: Int /** * Infinite loop that reads this atomic variable and performs the specified [action] on its value. */ -public inline fun AtomicInt.loop(action: (Int) -> Unit): Nothing { while(true) { action(value) } } +public inline fun AtomicInt.loop(action: (Int) -> Unit): Nothing { + contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) } + do { action(value) } while(true) +} -public inline fun AtomicInt.tryUpdate(function: (Int) -> Int): Boolean = tryUpdate(function) { _, _ -> } +public inline fun AtomicInt.tryUpdate(function: (Int) -> Int): Boolean { + contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) } + return tryUpdate(function) { _, _ -> } +} -public inline fun AtomicInt.update(function: (Int) -> Int) : Unit = update(function) { _, _ -> } +public inline fun AtomicInt.update(function: (Int) -> Int) { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + update(function) { _, _ -> } +} /** * Updates variable atomically using the specified [function] of its value and returns its old value. */ -public inline fun AtomicInt.getAndUpdate(function: (Int) -> Int): Int = update(function) { old, _ -> old } +public inline fun AtomicInt.getAndUpdate(function: (Int) -> Int): Int { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { old, _ -> old } +} /** * Updates variable atomically using the specified [function] of its value and returns its new value. */ -public inline fun AtomicInt.updateAndGet(function: (Int) -> Int): Int = update(function) { _, new -> new } +public inline fun AtomicInt.updateAndGet(function: (Int) -> Int): Int { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { _, new -> new } +} @PublishedApi internal inline fun AtomicInt.update(function: (Int) -> Int, transform: (old: Int, new: Int) -> R): R { - while (true) { - tryUpdate(function) { old, new -> return transform(old, new) } + contract { + callsInPlace(function, InvocationKind.AT_LEAST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + loop { cur -> + val upd = function(value) + if(compareAndSet(cur, upd)) return transform(cur, upd) } } @PublishedApi internal inline fun AtomicInt.tryUpdate(function: (Int) -> Int, onUpdated: (old: Int, new: Int) -> Unit): Boolean { + contract { + callsInPlace(function, InvocationKind.EXACTLY_ONCE) + callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE) + } val cur = value val upd = function(cur) return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) } diff --git a/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicLong.kt b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicLong.kt index 00a90807600..edea8ed1176 100644 --- a/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicLong.kt +++ b/arrow-libs/core/arrow-atomic/src/commonMain/kotlin/arrow/atomic/AtomicLong.kt @@ -1,5 +1,11 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.atomic +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract + public expect class AtomicLong(initialValue: Long) { public fun get(): Long @@ -23,31 +29,55 @@ public var AtomicLong.value: Long /** * Infinite loop that reads this atomic variable and performs the specified [action] on its value. */ -public inline fun AtomicLong.loop(action: (Long) -> Unit): Nothing { while(true) { action(value) } } +public inline fun AtomicLong.loop(action: (Long) -> Unit): Nothing { + contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) } + do { action(value) } while(true) +} -public inline fun AtomicLong.tryUpdate(function: (Long) -> Long): Boolean = tryUpdate(function) { _, _ -> } +public inline fun AtomicLong.tryUpdate(function: (Long) -> Long): Boolean { + contract { callsInPlace(function, InvocationKind.EXACTLY_ONCE) } + return tryUpdate(function) { _, _ -> } +} -public inline fun AtomicLong.update(function: (Long) -> Long): Unit = update(function) { _, _ -> } +public inline fun AtomicLong.update(function: (Long) -> Long) { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + update(function) { _, _ -> } +} /** * Updates variable atomically using the specified [function] of its value and returns its old value. */ -public inline fun AtomicLong.getAndUpdate(function: (Long) -> Long): Long = update(function) { old, _ -> old } +public inline fun AtomicLong.getAndUpdate(function: (Long) -> Long): Long { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { old, _ -> old } +} /** * Updates variable atomically using the specified [function] of its value and returns its new value. */ -public inline fun AtomicLong.updateAndGet(function: (Long) -> Long): Long = update(function) { _, new -> new } +public inline fun AtomicLong.updateAndGet(function: (Long) -> Long): Long { + contract { callsInPlace(function, InvocationKind.AT_LEAST_ONCE) } + return update(function) { _, new -> new } +} @PublishedApi internal inline fun AtomicLong.update(function: (Long) -> Long, transform: (old: Long, new: Long) -> R): R { - while (true) { - tryUpdate(function) { old, new -> return transform(old, new) } + contract { + callsInPlace(function, InvocationKind.AT_LEAST_ONCE) + callsInPlace(transform, InvocationKind.AT_MOST_ONCE) + } + loop { cur -> + val upd = function(value) + if(compareAndSet(cur, upd)) return transform(cur, upd) } } @PublishedApi internal inline fun AtomicLong.tryUpdate(function: (Long) -> Long, onUpdated: (old: Long, new: Long) -> Unit): Boolean { + contract { + callsInPlace(function, InvocationKind.EXACTLY_ONCE) + callsInPlace(onUpdated, InvocationKind.AT_MOST_ONCE) + } val cur = value val upd = function(cur) return compareAndSet(cur, upd).also { if (it) onUpdated(cur, upd) } diff --git a/arrow-libs/core/arrow-autoclose/src/commonMain/kotlin/arrow/AutoCloseScope.kt b/arrow-libs/core/arrow-autoclose/src/commonMain/kotlin/arrow/AutoCloseScope.kt index aab04f323e9..a7e1383571c 100644 --- a/arrow-libs/core/arrow-autoclose/src/commonMain/kotlin/arrow/AutoCloseScope.kt +++ b/arrow-libs/core/arrow-autoclose/src/commonMain/kotlin/arrow/AutoCloseScope.kt @@ -1,7 +1,12 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow import arrow.atomic.Atomic import arrow.atomic.update +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.coroutines.cancellation.CancellationException /** @@ -61,6 +66,7 @@ import kotlin.coroutines.cancellation.CancellationException * */ public inline fun autoCloseScope(block: AutoCloseScope.() -> A): A { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } val scope = DefaultAutoCloseScope() var throwable: Throwable? = null return try { diff --git a/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index 7d92cb16508..588992c5ff2 100644 --- a/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -1,7 +1,11 @@ @file:JvmName("NonEmptyListHighArityKt") +@file:OptIn(ExperimentalContracts::class) package arrow.core +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.jvm.JvmName public inline fun NonEmptyList.zip( @@ -16,11 +20,15 @@ public inline fun NonEmptyList, t10: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -35,11 +43,15 @@ public inline fun NonEmptyL t10: NonEmptyList, t11: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -55,11 +67,15 @@ public inline fun NonE t11: NonEmptyList, t12: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -76,11 +92,15 @@ public inline fun t12: NonEmptyList, t13: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -98,11 +118,15 @@ public inline fun , t14: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head, t14.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -121,11 +145,15 @@ public inline fun , t15: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head, t14.head, t15.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -145,11 +173,15 @@ public inline fun , t16: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head, t14.head, t15.head, t16.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -170,11 +202,15 @@ public inline fun , t17: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head, t14.head, t15.head, t16.head, t17.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -196,11 +232,15 @@ public inline fun , t18: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head, t14.head, t15.head, t16.head, t17.head, t18.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail, t18.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail, t18.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -223,11 +263,15 @@ public inline fun , t19: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head, t14.head, t15.head, t16.head, t17.head, t18.head, t19.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail, t18.tail, t19.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail, t18.tail, t19.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19) + } ) +} public inline fun NonEmptyList.zip( t1: NonEmptyList, @@ -251,8 +295,12 @@ public inline fun , t20: NonEmptyList, map: (A, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20) -> R, -): NonEmptyList = - NonEmptyList( +): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList( map(head, t1.head, t2.head, t3.head, t4.head, t5.head, t6.head, t7.head, t8.head, t9.head, t10.head, t11.head, t12.head, t13.head, t14.head, t15.head, t16.head, t17.head, t18.head, t19.head, t20.head), - tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail, t18.tail, t19.tail, t20.tail, map) + tail.zip(t1.tail, t2.tail, t3.tail, t4.tail, t5.tail, t6.tail, t7.tail, t8.tail, t9.tail, t10.tail, t11.tail, t12.tail, t13.tail, t14.tail, t15.tail, t16.tail, t17.tail, t18.tail, t19.tail, t20.tail) { t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20 -> + map(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, t20) + } ) +} diff --git a/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt b/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt index cea740acf69..d214556be0c 100644 --- a/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt +++ b/arrow-libs/core/arrow-core-high-arity/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt @@ -6,7 +6,7 @@ package arrow.core.raise import arrow.core.NonEmptyList import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind.AT_MOST_ONCE +import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.experimental.ExperimentalTypeInference import kotlin.jvm.JvmMultifileClass @@ -26,20 +26,46 @@ public inline fun Raise.() -> T10, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + ) { _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) } @RaiseDSL @@ -57,21 +83,49 @@ public inline fun Raise @BuilderInference action11: RaiseAccumulate.() -> T11, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + ) { _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) } @RaiseDSL @@ -90,22 +144,52 @@ public inline fun @BuilderInference action12: RaiseAccumulate.() -> T12, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + ) { _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) } @RaiseDSL @@ -125,23 +209,55 @@ public inline fun .() -> T13, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + ) { _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) } @RaiseDSL @@ -162,24 +278,58 @@ public inline fun .() -> T14, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - val n = accumulating(action14) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value, n.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(action14, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + val a14: T14 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + { a14 = action14(this) }, + ) { _, _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) } @RaiseDSL @@ -201,25 +351,61 @@ public inline fun .() -> T15, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - val n = accumulating(action14) - val o = accumulating(action15) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value, n.value, o.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(action14, InvocationKind.EXACTLY_ONCE) + callsInPlace(action15, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + val a14: T14 + val a15: T15 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + { a14 = action14(this) }, + { a15 = action15(this) }, + ) { _, _, _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) } @RaiseDSL @@ -242,26 +428,64 @@ public inline fun .() -> T16, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - val n = accumulating(action14) - val o = accumulating(action15) - val p = accumulating(action16) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value, n.value, o.value, p.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(action14, InvocationKind.EXACTLY_ONCE) + callsInPlace(action15, InvocationKind.EXACTLY_ONCE) + callsInPlace(action16, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + val a14: T14 + val a15: T15 + val a16: T16 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + { a14 = action14(this) }, + { a15 = action15(this) }, + { a16 = action16(this) }, + ) { _, _, _, _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16) } @RaiseDSL @@ -285,27 +509,67 @@ public inline fun .() -> T17, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - val n = accumulating(action14) - val o = accumulating(action15) - val p = accumulating(action16) - val q = accumulating(action17) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value, n.value, o.value, p.value, q.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(action14, InvocationKind.EXACTLY_ONCE) + callsInPlace(action15, InvocationKind.EXACTLY_ONCE) + callsInPlace(action16, InvocationKind.EXACTLY_ONCE) + callsInPlace(action17, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + val a14: T14 + val a15: T15 + val a16: T16 + val a17: T17 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + { a14 = action14(this) }, + { a15 = action15(this) }, + { a16 = action16(this) }, + { a17 = action17(this) }, + ) { _, _, _, _, _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17) } @RaiseDSL @@ -330,28 +594,70 @@ public inline fun .() -> T18, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - val n = accumulating(action14) - val o = accumulating(action15) - val p = accumulating(action16) - val q = accumulating(action17) - val r = accumulating(action18) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value, n.value, o.value, p.value, q.value, r.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(action14, InvocationKind.EXACTLY_ONCE) + callsInPlace(action15, InvocationKind.EXACTLY_ONCE) + callsInPlace(action16, InvocationKind.EXACTLY_ONCE) + callsInPlace(action17, InvocationKind.EXACTLY_ONCE) + callsInPlace(action18, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + val a14: T14 + val a15: T15 + val a16: T16 + val a17: T17 + val a18: T18 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + { a14 = action14(this) }, + { a15 = action15(this) }, + { a16 = action16(this) }, + { a17 = action17(this) }, + { a18 = action18(this) }, + ) { _, _, _, _, _, _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18) } @RaiseDSL @@ -377,29 +683,73 @@ public inline fun .() -> T19, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - val n = accumulating(action14) - val o = accumulating(action15) - val p = accumulating(action16) - val q = accumulating(action17) - val r = accumulating(action18) - val s = accumulating(action19) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value, n.value, o.value, p.value, q.value, r.value, s.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(action14, InvocationKind.EXACTLY_ONCE) + callsInPlace(action15, InvocationKind.EXACTLY_ONCE) + callsInPlace(action16, InvocationKind.EXACTLY_ONCE) + callsInPlace(action17, InvocationKind.EXACTLY_ONCE) + callsInPlace(action18, InvocationKind.EXACTLY_ONCE) + callsInPlace(action19, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + val a14: T14 + val a15: T15 + val a16: T16 + val a17: T17 + val a18: T18 + val a19: T19 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + { a14 = action14(this) }, + { a15 = action15(this) }, + { a16 = action16(this) }, + { a17 = action17(this) }, + { a18 = action18(this) }, + { a19 = action19(this) }, + ) { _, _, _, _, _, _, _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19) } @RaiseDSL @@ -426,29 +776,75 @@ public inline fun .() -> T20, block: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20) -> R, ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } - return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) - val j = accumulating(action10) - val k = accumulating(action11) - val l = accumulating(action12) - val m = accumulating(action13) - val n = accumulating(action14) - val o = accumulating(action15) - val p = accumulating(action16) - val q = accumulating(action17) - val r = accumulating(action18) - val s = accumulating(action19) - val t = accumulating(action20) - block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value, j.value, k.value, l.value, m.value, n.value, o.value, p.value, q.value, r.value, s.value, t.value) + contract { + callsInPlace(action1, InvocationKind.EXACTLY_ONCE) + callsInPlace(action2, InvocationKind.EXACTLY_ONCE) + callsInPlace(action3, InvocationKind.EXACTLY_ONCE) + callsInPlace(action4, InvocationKind.EXACTLY_ONCE) + callsInPlace(action5, InvocationKind.EXACTLY_ONCE) + callsInPlace(action6, InvocationKind.EXACTLY_ONCE) + callsInPlace(action7, InvocationKind.EXACTLY_ONCE) + callsInPlace(action8, InvocationKind.EXACTLY_ONCE) + callsInPlace(action9, InvocationKind.EXACTLY_ONCE) + callsInPlace(action10, InvocationKind.EXACTLY_ONCE) + callsInPlace(action11, InvocationKind.EXACTLY_ONCE) + callsInPlace(action12, InvocationKind.EXACTLY_ONCE) + callsInPlace(action13, InvocationKind.EXACTLY_ONCE) + callsInPlace(action14, InvocationKind.EXACTLY_ONCE) + callsInPlace(action15, InvocationKind.EXACTLY_ONCE) + callsInPlace(action16, InvocationKind.EXACTLY_ONCE) + callsInPlace(action17, InvocationKind.EXACTLY_ONCE) + callsInPlace(action18, InvocationKind.EXACTLY_ONCE) + callsInPlace(action19, InvocationKind.EXACTLY_ONCE) + callsInPlace(action20, InvocationKind.EXACTLY_ONCE) + callsInPlace(block, InvocationKind.EXACTLY_ONCE) } + val a1: T1 + val a2: T2 + val a3: T3 + val a4: T4 + val a5: T5 + val a6: T6 + val a7: T7 + val a8: T8 + val a9: T9 + val a10: T10 + val a11: T11 + val a12: T12 + val a13: T13 + val a14: T14 + val a15: T15 + val a16: T16 + val a17: T17 + val a18: T18 + val a19: T19 + val a20: T20 + zipOrAccumulate( + { a1 = action1(this) }, + { a2 = action2(this) }, + { a3 = action3(this) }, + { a4 = action4(this) }, + { a5 = action5(this) }, + { a6 = action6(this) }, + { a7 = action7(this) }, + { a8 = action8(this) }, + { + zipOrAccumulate( + { a9 = action9(this) }, + { a10 = action10(this) }, + { a11 = action11(this) }, + { a12 = action12(this) }, + { a13 = action13(this) }, + { a14 = action14(this) }, + { a15 = action15(this) }, + { a16 = action16(this) }, + { a17 = action17(this) }, + { a18 = action18(this) }, + { a19 = action19(this) }, + { a20 = action20(this) }, + ) { _, _, _, _, _, _, _, _, _, _, _, _ -> } + } + ) { _, _, _, _, _, _, _, _, _ -> } + return block(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20) } diff --git a/arrow-libs/core/arrow-core/api/arrow-core.klib.api b/arrow-libs/core/arrow-core/api/arrow-core.klib.api index e4aa3f69e80..59815911b24 100644 --- a/arrow-libs/core/arrow-core/api/arrow-core.klib.api +++ b/arrow-libs/core/arrow-core/api/arrow-core.klib.api @@ -366,7 +366,6 @@ final value class <#A: out kotlin/Any?> arrow.core/NonEmptyList : arrow.core/Non final fun (): kotlin.collections/List<#A> // arrow.core/NonEmptyList.tail.|(){}[0] final fun <#A1: kotlin/Any?> align(arrow.core/NonEmptyList<#A1>): arrow.core/NonEmptyList> // arrow.core/NonEmptyList.align|align(arrow.core.NonEmptyList<0:0>){0§}[0] - final fun <#A1: kotlin/Any?> coflatMap(kotlin/Function1, #A1>): arrow.core/NonEmptyList<#A1> // arrow.core/NonEmptyList.coflatMap|coflatMap(kotlin.Function1,0:0>){0§}[0] final fun <#A1: kotlin/Any?> padZip(arrow.core/NonEmptyList<#A1>): arrow.core/NonEmptyList> // arrow.core/NonEmptyList.padZip|padZip(arrow.core.NonEmptyList<0:0>){0§}[0] final fun <#A1: kotlin/Any?> zip(arrow.core/NonEmptyList<#A1>): arrow.core/NonEmptyList> // arrow.core/NonEmptyList.zip|zip(arrow.core.NonEmptyList<0:0>){0§}[0] final fun contains(#A): kotlin/Boolean // arrow.core/NonEmptyList.contains|contains(1:0){}[0] @@ -398,6 +397,7 @@ final value class <#A: out kotlin/Any?> arrow.core/NonEmptyList : arrow.core/Non final inline fun <#A1: kotlin/Any?, #B1: kotlin/Any?, #C1: kotlin/Any?> zip(arrow.core/NonEmptyList<#A1>, arrow.core/NonEmptyList<#B1>, kotlin/Function3<#A, #A1, #B1, #C1>): arrow.core/NonEmptyList<#C1> // arrow.core/NonEmptyList.zip|zip(arrow.core.NonEmptyList<0:0>;arrow.core.NonEmptyList<0:1>;kotlin.Function3<1:0,0:0,0:1,0:2>){0§;1§;2§}[0] final inline fun <#A1: kotlin/Any?, #B1: kotlin/Any?> padZip(arrow.core/NonEmptyList<#A1>, kotlin/Function1<#A, #B1>, kotlin/Function1<#A1, #B1>, kotlin/Function2<#A, #A1, #B1>): arrow.core/NonEmptyList<#B1> // arrow.core/NonEmptyList.padZip|padZip(arrow.core.NonEmptyList<0:0>;kotlin.Function1<1:0,0:1>;kotlin.Function1<0:0,0:1>;kotlin.Function2<1:0,0:0,0:1>){0§;1§}[0] final inline fun <#A1: kotlin/Any?, #B1: kotlin/Any?> zip(arrow.core/NonEmptyList<#A1>, kotlin/Function2<#A, #A1, #B1>): arrow.core/NonEmptyList<#B1> // arrow.core/NonEmptyList.zip|zip(arrow.core.NonEmptyList<0:0>;kotlin.Function2<1:0,0:0,0:1>){0§;1§}[0] + final inline fun <#A1: kotlin/Any?> coflatMap(kotlin/Function1, #A1>): arrow.core/NonEmptyList<#A1> // arrow.core/NonEmptyList.coflatMap|coflatMap(kotlin.Function1,0:0>){0§}[0] final inline fun <#A1: kotlin/Any?> distinctBy(kotlin/Function1<#A, #A1>): arrow.core/NonEmptyList<#A> // arrow.core/NonEmptyList.distinctBy|distinctBy(kotlin.Function1<1:0,0:0>){0§}[0] final inline fun <#A1: kotlin/Any?> flatMap(kotlin/Function1<#A, arrow.core/NonEmptyCollection<#A1>>): arrow.core/NonEmptyList<#A1> // arrow.core/NonEmptyList.flatMap|flatMap(kotlin.Function1<1:0,arrow.core.NonEmptyCollection<0:0>>){0§}[0] final inline fun <#A1: kotlin/Any?> foldLeft(#A1, kotlin/Function2<#A1, #A, #A1>): #A1 // arrow.core/NonEmptyList.foldLeft|foldLeft(0:0;kotlin.Function2<0:0,1:0,0:0>){0§}[0] @@ -462,15 +462,14 @@ open class <#A: kotlin/Any?> arrow.core.raise/RaiseAccumulate : arrow.core.raise final val raise // arrow.core.raise/RaiseAccumulate.raise|{}raise[0] final fun (): arrow.core.raise/Raise> // arrow.core.raise/RaiseAccumulate.raise.|(){}[0] - final fun <#A1: kotlin/Any> ensureNotNullOrAccumulate(#A1?, kotlin/Function0<#A>) // arrow.core.raise/RaiseAccumulate.ensureNotNullOrAccumulate|ensureNotNullOrAccumulate(0:0?;kotlin.Function0<1:0>){0§}[0] final fun <#A1: kotlin/Any?> (arrow.core/Either<#A, #A1>).bindOrAccumulate(): arrow.core.raise/RaiseAccumulate.Value<#A1> // arrow.core.raise/RaiseAccumulate.bindOrAccumulate|bindOrAccumulate@arrow.core.Either<1:0,0:0>(){0§}[0] final fun <#A1: kotlin/Any?> (arrow.core/Either, #A1>).bindNel(): #A1 // arrow.core.raise/RaiseAccumulate.bindNel|bindNel@arrow.core.Either,0:0>(){0§}[0] final fun <#A1: kotlin/Any?> (arrow.core/Either, #A1>).bindNelOrAccumulate(): arrow.core.raise/RaiseAccumulate.Value<#A1> // arrow.core.raise/RaiseAccumulate.bindNelOrAccumulate|bindNelOrAccumulate@arrow.core.Either,0:0>(){0§}[0] final fun <#A1: kotlin/Any?> (kotlin.collections/Iterable>).bindAllOrAccumulate(): arrow.core.raise/RaiseAccumulate.Value> // arrow.core.raise/RaiseAccumulate.bindAllOrAccumulate|bindAllOrAccumulate@kotlin.collections.Iterable>(){0§}[0] final fun addErrors(kotlin.collections/Iterable<#A>) // arrow.core.raise/RaiseAccumulate.addErrors|addErrors(kotlin.collections.Iterable<1:0>){}[0] - final fun ensureOrAccumulate(kotlin/Boolean, kotlin/Function0<#A>) // arrow.core.raise/RaiseAccumulate.ensureOrAccumulate|ensureOrAccumulate(kotlin.Boolean;kotlin.Function0<1:0>){}[0] final fun hasErrors(): kotlin/Boolean // arrow.core.raise/RaiseAccumulate.hasErrors|hasErrors(){}[0] final fun raiseErrors(): kotlin/Nothing // arrow.core.raise/RaiseAccumulate.raiseErrors|raiseErrors(){}[0] + final inline fun <#A1: kotlin/Any> ensureNotNullOrAccumulate(#A1?, kotlin/Function0<#A>) // arrow.core.raise/RaiseAccumulate.ensureNotNullOrAccumulate|ensureNotNullOrAccumulate(0:0?;kotlin.Function0<1:0>){0§}[0] final inline fun <#A1: kotlin/Any?, #B1: kotlin/Any?, #C1: kotlin/Any?> (kotlin.collections/Map<#A1, #B1>).mapOrAccumulate(kotlin/Function2, kotlin.collections/Map.Entry<#A1, #B1>, #C1>): kotlin.collections/List<#C1> // arrow.core.raise/RaiseAccumulate.mapOrAccumulate|mapOrAccumulate@kotlin.collections.Map<0:0,0:1>(kotlin.Function2,kotlin.collections.Map.Entry<0:0,0:1>,0:2>){0§;1§;2§}[0] final inline fun <#A1: kotlin/Any?, #B1: kotlin/Any?, #C1: kotlin/Any?> (kotlin.collections/Map<#A1, #B1>).mapValuesOrAccumulate(kotlin/Function2, kotlin.collections/Map.Entry<#A1, #B1>, #C1>): kotlin.collections/Map<#A1, #C1> // arrow.core.raise/RaiseAccumulate.mapValuesOrAccumulate|mapValuesOrAccumulate@kotlin.collections.Map<0:0,0:1>(kotlin.Function2,kotlin.collections.Map.Entry<0:0,0:1>,0:2>){0§;1§;2§}[0] final inline fun <#A1: kotlin/Any?, #B1: kotlin/Any?> (arrow.core/NonEmptyList<#A1>).mapOrAccumulate(kotlin/Function2, #A1, #B1>): arrow.core/NonEmptyList<#B1> // arrow.core.raise/RaiseAccumulate.mapOrAccumulate|mapOrAccumulate@arrow.core.NonEmptyList<0:0>(kotlin.Function2,0:0,0:1>){0§;1§}[0] @@ -482,6 +481,7 @@ open class <#A: kotlin/Any?> arrow.core.raise/RaiseAccumulate : arrow.core.raise final inline fun <#A1: kotlin/Any?> (arrow.core.raise/RaiseAccumulate.Value<#A1>).getValue(kotlin/Nothing?, kotlin.reflect/KProperty<*>): #A1 // arrow.core.raise/RaiseAccumulate.getValue|getValue@arrow.core.raise.RaiseAccumulate.Value<0:0>(kotlin.Nothing?;kotlin.reflect.KProperty<*>){0§}[0] final inline fun <#A1: kotlin/Any?> accumulating(kotlin/Function1, #A1>): arrow.core.raise/RaiseAccumulate.Value<#A1> // arrow.core.raise/RaiseAccumulate.accumulating|accumulating(kotlin.Function1,0:0>){0§}[0] final inline fun <#A1: kotlin/Any?> withNel(kotlin/Function1>, #A1>): #A1 // arrow.core.raise/RaiseAccumulate.withNel|withNel(kotlin.Function1>,0:0>){0§}[0] + final inline fun ensureOrAccumulate(kotlin/Boolean, kotlin/Function0<#A>) // arrow.core.raise/RaiseAccumulate.ensureOrAccumulate|ensureOrAccumulate(kotlin.Boolean;kotlin.Function0<1:0>){}[0] open fun <#A1: kotlin/Any?, #B1: kotlin/Any?> (kotlin.collections/Map<#A1, arrow.core/Either<#A, #B1>>).bindAll(): kotlin.collections/Map<#A1, #B1> // arrow.core.raise/RaiseAccumulate.bindAll|bindAll@kotlin.collections.Map<0:0,arrow.core.Either<1:0,0:1>>(){0§;1§}[0] open fun <#A1: kotlin/Any?> (arrow.core/NonEmptyList>).bindAll(): arrow.core/NonEmptyList<#A1> // arrow.core.raise/RaiseAccumulate.bindAll|bindAll@arrow.core.NonEmptyList>(){0§}[0] open fun <#A1: kotlin/Any?> (arrow.core/NonEmptySet>).bindAll(): arrow.core/NonEmptySet<#A1> // arrow.core.raise/RaiseAccumulate.bindAll|bindAll@arrow.core.NonEmptySet>(){0§}[0] @@ -730,7 +730,6 @@ final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?> ( final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?> (kotlin.sequences/Sequence<#A>).arrow.core/zip(kotlin.sequences/Sequence<#B>, kotlin.sequences/Sequence<#C>, kotlin/Function3<#A, #B, #C, #D>): kotlin.sequences/Sequence<#D> // arrow.core/zip|zip@kotlin.sequences.Sequence<0:0>(kotlin.sequences.Sequence<0:1>;kotlin.sequences.Sequence<0:2>;kotlin.Function3<0:0,0:1,0:2,0:3>){0§;1§;2§;3§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin/Any?> (kotlin/Triple<#A, #B, #C>).arrow.core/plus(#D): arrow.core/Tuple4<#A, #B, #C, #D> // arrow.core/plus|plus@kotlin.Triple<0:0,0:1,0:2>(0:3){0§;1§;2§;3§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/Either<#A, #B>).arrow.core/handleErrorWith(kotlin/Function1<#A, arrow.core/Either<#C, #B>>): arrow.core/Either<#C, #B> // arrow.core/handleErrorWith|handleErrorWith@arrow.core.Either<0:0,0:1>(kotlin.Function1<0:0,arrow.core.Either<0:2,0:1>>){0§;1§;2§}[0] -final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/NonEmptyList<#C>).arrow.core/unzip(kotlin/Function1<#C, kotlin/Pair<#A, #B>>): kotlin/Pair, arrow.core/NonEmptyList<#B>> // arrow.core/unzip|unzip@arrow.core.NonEmptyList<0:2>(kotlin.Function1<0:2,kotlin.Pair<0:0,0:1>>){0§;1§;2§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin.collections/Iterable<#A>).arrow.core/crosswalkMap(kotlin/Function1<#A, kotlin.collections/Map<#B, #C>>): kotlin.collections/Map<#B, kotlin.collections/List<#C>> // arrow.core/crosswalkMap|crosswalkMap@kotlin.collections.Iterable<0:0>(kotlin.Function1<0:0,kotlin.collections.Map<0:1,0:2>>){0§;1§;2§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin.collections/Map<#A, #B>).arrow.core/align(kotlin.collections/Map<#A, #C>): kotlin.collections/Map<#A, arrow.core/Ior<#B, #C>> // arrow.core/align|align@kotlin.collections.Map<0:0,0:1>(kotlin.collections.Map<0:0,0:2>){0§;1§;2§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin.collections/Map<#A, #B>).arrow.core/flatMapValues(kotlin/Function1, kotlin.collections/Map<#A, #C>>): kotlin.collections/Map<#A, #C> // arrow.core/flatMapValues|flatMapValues@kotlin.collections.Map<0:0,0:1>(kotlin.Function1,kotlin.collections.Map<0:0,0:2>>){0§;1§;2§}[0] @@ -751,10 +750,8 @@ final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin.sequences/ final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin/Function1, #C>).arrow.core.raise/mapError(kotlin/Function1<#A, #B>): kotlin/Function1, #C> // arrow.core.raise/mapError|mapError@kotlin.Function1,0:2>(kotlin.Function1<0:0,0:1>){0§;1§;2§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin/Function1, #C>).arrow.core.raise/recover(kotlin/Function2, #A, #C>): kotlin/Function1, #C> // arrow.core.raise/recover|recover@kotlin.Function1,0:2>(kotlin.Function2,0:0,0:2>){0§;1§;2§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin/Pair<#A, #B>).arrow.core/plus(#C): kotlin/Triple<#A, #B, #C> // arrow.core/plus|plus@kotlin.Pair<0:0,0:1>(0:2){0§;1§;2§}[0] -final fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Either<#A, #B>).arrow.core/combine(arrow.core/Either<#A, #B>, kotlin/Function2<#A, #A, #A>, kotlin/Function2<#B, #B, #B>): arrow.core/Either<#A, #B> // arrow.core/combine|combine@arrow.core.Either<0:0,0:1>(arrow.core.Either<0:0,0:1>;kotlin.Function2<0:0,0:0,0:0>;kotlin.Function2<0:1,0:1,0:1>){0§;1§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Either<#A, #B>).arrow.core/toEitherNel(): arrow.core/Either, #B> // arrow.core/toEitherNel|toEitherNel@arrow.core.Either<0:0,0:1>(){0§;1§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Either<#A, arrow.core/Either<#A, #B>>).arrow.core/flatten(): arrow.core/Either<#A, #B> // arrow.core/flatten|flatten@arrow.core.Either<0:0,arrow.core.Either<0:0,0:1>>(){0§;1§}[0] -final fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Ior<#A, #B>).arrow.core/combine(arrow.core/Ior<#A, #B>, kotlin/Function2<#A, #A, #A>, kotlin/Function2<#B, #B, #B>): arrow.core/Ior<#A, #B> // arrow.core/combine|combine@arrow.core.Ior<0:0,0:1>(arrow.core.Ior<0:0,0:1>;kotlin.Function2<0:0,0:0,0:0>;kotlin.Function2<0:1,0:1,0:1>){0§;1§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Ior<#A, #B>).arrow.core/toIorNel(): arrow.core/Ior, #B> // arrow.core/toIorNel|toIorNel@arrow.core.Ior<0:0,0:1>(){0§;1§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/NonEmptyList>).arrow.core/unzip(): kotlin/Pair, arrow.core/NonEmptyList<#B>> // arrow.core/unzip|unzip@arrow.core.NonEmptyList>(){0§;1§}[0] final fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Option>).arrow.core/toMap(): kotlin.collections/Map<#A, #B> // arrow.core/toMap|toMap@arrow.core.Option>(){0§;1§}[0] @@ -804,7 +801,6 @@ final fun <#A: kotlin/Any?> (#A).arrow.core/some(): arrow.core/Option<#A> // arr final fun <#A: kotlin/Any?> (#A?).arrow.core/toOption(): arrow.core/Option<#A> // arrow.core/toOption|toOption@0:0?(){0§}[0] final fun <#A: kotlin/Any?> (arrow.core/Either<#A, #A>).arrow.core/merge(): #A // arrow.core/merge|merge@arrow.core.Either<0:0,0:0>(){0§}[0] final fun <#A: kotlin/Any?> (arrow.core/NonEmptyList>).arrow.core/flatten(): arrow.core/NonEmptyList<#A> // arrow.core/flatten|flatten@arrow.core.NonEmptyList>(){0§}[0] -final fun <#A: kotlin/Any?> (arrow.core/Option<#A>).arrow.core/combine(arrow.core/Option<#A>, kotlin/Function2<#A, #A, #A>): arrow.core/Option<#A> // arrow.core/combine|combine@arrow.core.Option<0:0>(arrow.core.Option<0:0>;kotlin.Function2<0:0,0:0,0:0>){0§}[0] final fun <#A: kotlin/Any?> (arrow.core/Option>).arrow.core/flatten(): arrow.core/Option<#A> // arrow.core/flatten|flatten@arrow.core.Option>(){0§}[0] final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).arrow.core/collectionSizeOrDefault(kotlin/Int): kotlin/Int // arrow.core/collectionSizeOrDefault|collectionSizeOrDefault@kotlin.collections.Iterable<0:0>(kotlin.Int){0§}[0] final fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).arrow.core/elementAtOrNone(kotlin/Int): arrow.core/Option<#A> // arrow.core/elementAtOrNone|elementAtOrNone@kotlin.collections.Iterable<0:0>(kotlin.Int){0§}[0] @@ -929,6 +925,7 @@ final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/Ior<#A, #B>).arrow.core/handleErrorWith(kotlin/Function2<#B, #B, #B>, kotlin/Function1<#A, arrow.core/Ior<#C, #B>>): arrow.core/Ior<#C, #B> // arrow.core/handleErrorWith|handleErrorWith@arrow.core.Ior<0:0,0:1>(kotlin.Function2<0:1,0:1,0:1>;kotlin.Function1<0:0,arrow.core.Ior<0:2,0:1>>){0§;1§;2§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/NonEmptyList<#B>).arrow.core/mapOrAccumulate(kotlin/Function2<#A, #A, #A>, kotlin/Function2, #B, #C>): arrow.core/Either<#A, arrow.core/NonEmptyList<#C>> // arrow.core/mapOrAccumulate|mapOrAccumulate@arrow.core.NonEmptyList<0:1>(kotlin.Function2<0:0,0:0,0:0>;kotlin.Function2,0:1,0:2>){0§;1§;2§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/NonEmptyList<#B>).arrow.core/mapOrAccumulate(kotlin/Function2, #B, #C>): arrow.core/Either, arrow.core/NonEmptyList<#C>> // arrow.core/mapOrAccumulate|mapOrAccumulate@arrow.core.NonEmptyList<0:1>(kotlin.Function2,0:1,0:2>){0§;1§;2§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/NonEmptyList<#C>).arrow.core/unzip(kotlin/Function1<#C, kotlin/Pair<#A, #B>>): kotlin/Pair, arrow.core/NonEmptyList<#B>> // arrow.core/unzip|unzip@arrow.core.NonEmptyList<0:2>(kotlin.Function1<0:2,kotlin.Pair<0:0,0:1>>){0§;1§;2§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/NonEmptySet<#B>).arrow.core/mapOrAccumulate(kotlin/Function2<#A, #A, #A>, kotlin/Function2, #B, #C>): arrow.core/Either<#A, arrow.core/NonEmptySet<#C>> // arrow.core/mapOrAccumulate|mapOrAccumulate@arrow.core.NonEmptySet<0:1>(kotlin.Function2<0:0,0:0,0:0>;kotlin.Function2,0:1,0:2>){0§;1§;2§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (arrow.core/NonEmptySet<#B>).arrow.core/mapOrAccumulate(kotlin/Function2, #B, #C>): arrow.core/Either, arrow.core/NonEmptySet<#C>> // arrow.core/mapOrAccumulate|mapOrAccumulate@arrow.core.NonEmptySet<0:1>(kotlin.Function2,0:1,0:2>){0§;1§;2§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> (kotlin.collections/Iterable<#A>).arrow.core/align(kotlin.collections/Iterable<#B>, kotlin/Function1, #C>): kotlin.collections/List<#C> // arrow.core/align|align@kotlin.collections.Iterable<0:0>(kotlin.collections.Iterable<0:1>;kotlin.Function1,0:2>){0§;1§;2§}[0] @@ -957,7 +954,9 @@ final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core.raise/Raise (arrow.core.raise/Raise>).arrow.core.raise/forEachAccumulating(kotlin.collections/Iterator<#B>, kotlin/Function2, #B, kotlin/Unit>) // arrow.core.raise/forEachAccumulating|forEachAccumulating@arrow.core.raise.Raise>(kotlin.collections.Iterator<0:1>;kotlin.Function2,0:1,kotlin.Unit>){0§;1§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core.raise/Raise>).arrow.core.raise/forEachAccumulating(kotlin.sequences/Sequence<#B>, kotlin/Function2, #B, kotlin/Unit>) // arrow.core.raise/forEachAccumulating|forEachAccumulating@arrow.core.raise.Raise>(kotlin.sequences.Sequence<0:1>;kotlin.Function2,0:1,kotlin.Unit>){0§;1§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core.raise/Raise>).arrow.core.raise/forEachAccumulatingImpl(kotlin.collections/Iterator<#B>, kotlin/Function3, #B, kotlin/Boolean, kotlin/Unit>) // arrow.core.raise/forEachAccumulatingImpl|forEachAccumulatingImpl@arrow.core.raise.Raise>(kotlin.collections.Iterator<0:1>;kotlin.Function3,0:1,kotlin.Boolean,kotlin.Unit>){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Either<#A, #B>).arrow.core/combine(arrow.core/Either<#A, #B>, kotlin/Function2<#A, #A, #A>, kotlin/Function2<#B, #B, #B>): arrow.core/Either<#A, #B> // arrow.core/combine|combine@arrow.core.Either<0:0,0:1>(arrow.core.Either<0:0,0:1>;kotlin.Function2<0:0,0:0,0:0>;kotlin.Function2<0:1,0:1,0:1>){0§;1§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Either<#A, #B>).arrow.core/getOrElse(kotlin/Function1<#A, #B>): #B // arrow.core/getOrElse|getOrElse@arrow.core.Either<0:0,0:1>(kotlin.Function1<0:0,0:1>){0§;1§}[0] +final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Ior<#A, #B>).arrow.core/combine(arrow.core/Ior<#A, #B>, kotlin/Function2<#A, #A, #A>, kotlin/Function2<#B, #B, #B>): arrow.core/Ior<#A, #B> // arrow.core/combine|combine@arrow.core.Ior<0:0,0:1>(arrow.core.Ior<0:0,0:1>;kotlin.Function2<0:0,0:0,0:0>;kotlin.Function2<0:1,0:1,0:1>){0§;1§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Ior<#A, #B>).arrow.core/getOrElse(kotlin/Function1<#A, #B>): #B // arrow.core/getOrElse|getOrElse@arrow.core.Ior<0:0,0:1>(kotlin.Function1<0:0,0:1>){0§;1§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (arrow.core/Ior<#A, arrow.core/Ior<#A, #B>>).arrow.core/flatten(kotlin/Function2<#A, #A, #A>): arrow.core/Ior<#A, #B> // arrow.core/flatten|flatten@arrow.core.Ior<0:0,arrow.core.Ior<0:0,0:1>>(kotlin.Function2<0:0,0:0,0:0>){0§;1§}[0] final inline fun <#A: kotlin/Any?, #B: kotlin/Any?> (kotlin.collections/Iterable<#A>).arrow.core/reduceOrNull(kotlin/Function1<#A, #B>, kotlin/Function2<#B, #A, #B>): #B? // arrow.core/reduceOrNull|reduceOrNull@kotlin.collections.Iterable<0:0>(kotlin.Function1<0:0,0:1>;kotlin.Function2<0:1,0:0,0:1>){0§;1§}[0] @@ -980,6 +979,7 @@ final inline fun <#A: kotlin/Any?, #B: reified kotlin/Any?> (kotlin.collections/ final inline fun <#A: kotlin/Any?, #B: reified kotlin/Throwable, #C: kotlin/Any?> (arrow.core/Either).arrow.core/catch(kotlin/Function2, #B, #C>): arrow.core/Either<#A, #C> // arrow.core/catch|catch@arrow.core.Either(kotlin.Function2,0:1,0:2>){0§;1§;2§}[0] final inline fun <#A: kotlin/Any?> (#A).arrow.core/nel(): arrow.core/NonEmptyList<#A> // arrow.core/nel|nel@0:0(){0§}[0] final inline fun <#A: kotlin/Any?> (arrow.core.raise/Raise<#A>).arrow.core.raise/ensure(kotlin/Boolean, kotlin/Function0<#A>) // arrow.core.raise/ensure|ensure@arrow.core.raise.Raise<0:0>(kotlin.Boolean;kotlin.Function0<0:0>){0§}[0] +final inline fun <#A: kotlin/Any?> (arrow.core/Option<#A>).arrow.core/combine(arrow.core/Option<#A>, kotlin/Function2<#A, #A, #A>): arrow.core/Option<#A> // arrow.core/combine|combine@arrow.core.Option<0:0>(arrow.core.Option<0:0>;kotlin.Function2<0:0,0:0,0:0>){0§}[0] final inline fun <#A: kotlin/Any?> (arrow.core/Option<#A>).arrow.core/getOrElse(kotlin/Function0<#A>): #A // arrow.core/getOrElse|getOrElse@arrow.core.Option<0:0>(kotlin.Function0<0:0>){0§}[0] final inline fun <#A: kotlin/Any?> (arrow.core/Option<#A>).arrow.core/recover(kotlin/Function1, #A>): arrow.core/Option<#A> // arrow.core/recover|recover@arrow.core.Option<0:0>(kotlin.Function1,0:0>){0§}[0] final inline fun <#A: kotlin/Any?> (kotlin.collections/Iterable<#A>).arrow.core/firstOrNone(kotlin/Function1<#A, kotlin/Boolean>): arrow.core/Option<#A> // arrow.core/firstOrNone|firstOrNone@kotlin.collections.Iterable<0:0>(kotlin.Function1<0:0,kotlin.Boolean>){0§}[0] diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt index 1ae97e13681..b38b2da2595 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt @@ -528,7 +528,10 @@ public sealed class Either { * */ public inline fun isLeft(predicate: (A) -> Boolean): Boolean { - contract { returns(true) implies (this@Either is Left) } + contract { + returns(true) implies (this@Either is Left) + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) + } return this@Either is Left && predicate(value) } @@ -554,7 +557,10 @@ public sealed class Either { * */ public inline fun isRight(predicate: (B) -> Boolean): Boolean { - contract { returns(true) implies (this@Either is Right) } + contract { + returns(true) implies (this@Either is Right) + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) + } return this@Either is Right && predicate(value) } @@ -799,12 +805,16 @@ public sealed class Either { public companion object { @JvmStatic - public inline fun catch(f: () -> R): Either = - arrow.core.raise.catch({ f().right() }) { it.left() } + public inline fun catch(f: () -> R): Either { + contract { callsInPlace(f, InvocationKind.AT_MOST_ONCE) } + return arrow.core.raise.catch({ f().right() }) { it.left() } + } @JvmStatic - public inline fun catchOrThrow(f: () -> R): Either = - arrow.core.raise.catch>({ f().right() }) { it.left() } + public inline fun catchOrThrow(f: () -> R): Either { + contract { callsInPlace(f, InvocationKind.AT_MOST_ONCE) } + return arrow.core.raise.catch>({ f().right() }) { it.left() } + } public inline fun zipOrAccumulate( combine: (E, E) -> E, @@ -1369,8 +1379,12 @@ public operator fun , B : Comparable> Either.compareT * If both are [Right] then combine both [B] values using [combineRight] or if both are [Left] then combine both [A] values using [combineLeft], * otherwise return the sole [Left] value (either `this` or [other]). */ -public fun Either.combine(other: Either, combineLeft: (A, A) -> A, combineRight: (B, B) -> B): Either = - when (val one = this) { +public inline fun Either.combine(other: Either, combineLeft: (A, A) -> A, combineRight: (B, B) -> B): Either { + contract { + callsInPlace(combineLeft, InvocationKind.AT_MOST_ONCE) + callsInPlace(combineRight, InvocationKind.AT_MOST_ONCE) + } + return when (val one = this) { is Left -> when (other) { is Left -> Left(combineLeft(one.value, other.value)) is Right -> one @@ -1381,6 +1395,7 @@ public fun Either.combine(other: Either, combineLeft: (A, A) is Right -> Right(combineRight(one.value, other.value)) } } +} public const val NicheAPI: String = "This API is niche and will be removed in the future. If this method is crucial for you, please let us know on the Arrow Github. Thanks!\n https://github.com/arrow-kt/arrow/issues\n" diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Ior.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Ior.kt index 8357e647716..788030e6414 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Ior.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Ior.kt @@ -339,6 +339,7 @@ public sealed class Ior { contract { returns(true) implies (this@Ior is Left) returns(false) implies (this@Ior is Right || this@Ior is Both) + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) } return this@Ior is Left && predicate(value) } @@ -364,6 +365,7 @@ public sealed class Ior { contract { returns(true) implies (this@Ior is Right) returns(false) implies (this@Ior is Left || this@Ior is Both) + callsInPlace(predicate, InvocationKind.AT_MOST_ONCE) } return this@Ior is Right && predicate(value) } @@ -390,6 +392,8 @@ public sealed class Ior { contract { returns(true) implies (this@Ior is Both) returns(false) implies (this@Ior is Left || this@Ior is Right) + callsInPlace(leftPredicate, InvocationKind.AT_MOST_ONCE) + callsInPlace(rightPredicate, InvocationKind.AT_MOST_ONCE) } return this@Ior is Both && leftPredicate(leftValue) && rightPredicate(rightValue) } @@ -400,8 +404,12 @@ public sealed class Ior { * * @param f The function to bind across [Ior.Right]. */ -public inline fun Ior.flatMap(combine: (A, A) -> A, f: (B) -> Ior): Ior = - when (this) { +public inline fun Ior.flatMap(combine: (A, A) -> A, f: (B) -> Ior): Ior { + contract { + callsInPlace(combine, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.AT_MOST_ONCE) + } + return when (this) { is Left -> this is Right -> f(value) is Both -> when (val r = f(rightValue)) { @@ -410,14 +418,19 @@ public inline fun Ior.flatMap(combine: (A, A) -> A, f: (B) -> Io is Both -> Both(combine(this.leftValue, r.leftValue), r.rightValue) } } +} /** * Binds the given function across [Ior.Left]. * * @param f The function to bind across [Ior.Left]. */ -public inline fun Ior.handleErrorWith(combine: (B, B) -> B, f: (A) -> Ior): Ior = - when (this) { +public inline fun Ior.handleErrorWith(combine: (B, B) -> B, f: (A) -> Ior): Ior { + contract { + callsInPlace(combine, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.AT_MOST_ONCE) + } + return when (this) { is Left -> f(value) is Right -> this is Both -> when (val l = f(leftValue)) { @@ -426,6 +439,7 @@ public inline fun Ior.handleErrorWith(combine: (B, B) -> B, f: ( is Both -> Both(l.leftValue, combine(this.rightValue, l.rightValue)) } } +} public inline fun Ior.getOrElse(default: (A) -> B): B { contract { callsInPlace(default, InvocationKind.AT_MOST_ONCE) } @@ -443,8 +457,12 @@ public fun A.leftIor(): Ior = Ior.Left(this) public fun A.rightIor(): Ior = Ior.Right(this) -public fun Ior.combine(other: Ior, combineA: (A, A) -> A, combineB: (B, B) -> B): Ior = - when (this) { +public inline fun Ior.combine(other: Ior, combineA: (A, A) -> A, combineB: (B, B) -> B): Ior { + contract { + callsInPlace(combineA, InvocationKind.AT_MOST_ONCE) + callsInPlace(combineB, InvocationKind.AT_MOST_ONCE) + } + return when (this) { is Ior.Left -> when (other) { is Ior.Left -> Ior.Left(combineA(value, other.value)) is Ior.Right -> Ior.Both(value, other.value) @@ -463,9 +481,12 @@ public fun Ior.combine(other: Ior, combineA: (A, A) -> A, com is Ior.Both -> Ior.Both(combineA(leftValue, other.leftValue), combineB(rightValue, other.rightValue)) } } +} -public inline fun Ior>.flatten(combine: (A, A) -> A): Ior = - flatMap(combine, ::identity) +public inline fun Ior>.flatten(combine: (A, A) -> A): Ior { + contract { callsInPlace(combine, InvocationKind.AT_MOST_ONCE) } + return flatMap(combine, ::identity) +} /** * Given an [Ior] with an error type [A], returns an [IorNel] with the same diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt index 546ec1c3627..2f420636d6d 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/NonEmptyList.kt @@ -1,12 +1,15 @@ -@file:OptIn(ExperimentalTypeInference::class) +@file:OptIn(ExperimentalTypeInference::class, ExperimentalContracts::class) package arrow.core import arrow.core.raise.RaiseAccumulate -import kotlin.collections.unzip as stdlibUnzip +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.experimental.ExperimentalTypeInference import kotlin.jvm.JvmInline import kotlin.jvm.JvmName +import kotlin.collections.unzip as stdlibUnzip public typealias Nel = NonEmptyList @@ -209,17 +212,23 @@ public value class NonEmptyList @PublishedApi internal constructor( public override operator fun plus(element: @UnsafeVariance A): NonEmptyList = NonEmptyList(all + element) - public inline fun foldLeft(b: B, f: (B, A) -> B): B = - all.fold(b, f) + public inline fun foldLeft(b: B, f: (B, A) -> B): B { + contract { callsInPlace(f, InvocationKind.AT_LEAST_ONCE) } + var accumulator = f(b, head) + for (element in tail) accumulator = f(accumulator, element) + return accumulator + } - public fun coflatMap(f: (NonEmptyList) -> B): NonEmptyList = - buildList { - var current = all - while (current.isNotEmpty()) { - add(f(NonEmptyList(current))) - current = current.drop(1) - } + public inline fun coflatMap(f: (NonEmptyList) -> B): NonEmptyList { + contract { callsInPlace(f, InvocationKind.AT_LEAST_ONCE) } + var current = this + return buildList { + do { + add(f(current)) + current = current.drop(1).toNonEmptyListOrNull() ?: break + } while (true) }.let(::NonEmptyList) + } public fun extract(): A = this.head @@ -233,8 +242,10 @@ public value class NonEmptyList @PublishedApi internal constructor( public fun padZip(other: NonEmptyList): NonEmptyList> = padZip(other, { it to null }, { null to it }, { a, b -> a to b }) - public inline fun padZip(other: NonEmptyList, left: (A) -> C, right: (B) -> C, both: (A, B) -> C): NonEmptyList = - NonEmptyList(both(head, other.head), tail.padZip(other.tail, left, right, both)) + public inline fun padZip(other: NonEmptyList, left: (A) -> C, right: (B) -> C, both: (A, B) -> C): NonEmptyList { + contract { callsInPlace(both, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(both(head, other.head), tail.padZip(other.tail, left, right) { a, b -> both(a, b) }) + } public companion object { @PublishedApi @@ -248,23 +259,29 @@ public value class NonEmptyList @PublishedApi internal constructor( public inline fun zip( b: NonEmptyList, map: (A, B) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head), tail.zip(b.tail) { a, bb -> map(a, bb) }) + } public inline fun zip( b: NonEmptyList, c: NonEmptyList, map: (A, B, C) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head), tail.zip(b.tail, c.tail) { a, bb, cc -> map(a, bb, cc) }) + } public inline fun zip( b: NonEmptyList, c: NonEmptyList, d: NonEmptyList, map: (A, B, C, D) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, d.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head, d.head), tail.zip(b.tail, c.tail, d.tail) { a, bb, cc, dd -> map(a, bb, cc, dd) }) + } public inline fun zip( b: NonEmptyList, @@ -272,8 +289,10 @@ public value class NonEmptyList @PublishedApi internal constructor( d: NonEmptyList, e: NonEmptyList, map: (A, B, C, D, E) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, d.all, e.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head, d.head, e.head), tail.zip(b.tail, c.tail, d.tail, e.tail) { a, bb, cc, dd, ee -> map(a, bb, cc, dd, ee) }) + } public inline fun zip( b: NonEmptyList, @@ -282,8 +301,10 @@ public value class NonEmptyList @PublishedApi internal constructor( e: NonEmptyList, f: NonEmptyList, map: (A, B, C, D, E, F) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head, d.head, e.head, f.head), tail.zip(b.tail, c.tail, d.tail, e.tail, f.tail) { a, bb, cc, dd, ee, ff -> map(a, bb, cc, dd, ee, ff) }) + } public inline fun zip( b: NonEmptyList, @@ -293,8 +314,10 @@ public value class NonEmptyList @PublishedApi internal constructor( f: NonEmptyList, g: NonEmptyList, map: (A, B, C, D, E, F, G) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head, d.head, e.head, f.head, g.head), tail.zip(b.tail, c.tail, d.tail, e.tail, f.tail, g.tail) { a, bb, cc, dd, ee, ff, gg -> map(a, bb, cc, dd, ee, ff, gg) }) + } public inline fun zip( b: NonEmptyList, @@ -305,8 +328,10 @@ public value class NonEmptyList @PublishedApi internal constructor( g: NonEmptyList, h: NonEmptyList, map: (A, B, C, D, E, F, G, H) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head, d.head, e.head, f.head, g.head, h.head), tail.zip(b.tail, c.tail, d.tail, e.tail, f.tail, g.tail, h.tail) { a, bb, cc, dd, ee, ff, gg, hh -> map(a, bb, cc, dd, ee, ff, gg, hh) }) + } public inline fun zip( b: NonEmptyList, @@ -318,8 +343,10 @@ public value class NonEmptyList @PublishedApi internal constructor( h: NonEmptyList, i: NonEmptyList, map: (A, B, C, D, E, F, G, H, I) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, i.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head, d.head, e.head, f.head, g.head, h.head, i.head), tail.zip(b.tail, c.tail, d.tail, e.tail, f.tail, g.tail, h.tail, i.tail) { a, bb, cc, dd, ee, ff, gg, hh, ii -> map(a, bb, cc, dd, ee, ff, gg, hh, ii) }) + } public inline fun zip( b: NonEmptyList, @@ -332,8 +359,10 @@ public value class NonEmptyList @PublishedApi internal constructor( i: NonEmptyList, j: NonEmptyList, map: (A, B, C, D, E, F, G, H, I, J) -> Z - ): NonEmptyList = - NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, i.all, j.all, map)) + ): NonEmptyList { + contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) } + return NonEmptyList(map(head, b.head, c.head, d.head, e.head, f.head, g.head, h.head, i.head, j.head), tail.zip(b.tail, c.tail, d.tail, e.tail, f.tail, g.tail, h.tail, i.tail, j.tail) { a, bb, cc, dd, ee, ff, gg, hh, ii, jj -> map(a, bb, cc, dd, ee, ff, gg, hh, ii, jj) }) + } } @JvmName("nonEmptyListOf") @@ -368,10 +397,13 @@ public inline fun > NonEmptyList.max(): T = public fun NonEmptyList>.unzip(): Pair, NonEmptyList> = this.unzip(::identity) -public fun NonEmptyList.unzip(f: (C) -> Pair): Pair, NonEmptyList> = - map(f).stdlibUnzip().let { (l1, l2) -> +@Suppress("WRONG_INVOCATION_KIND") +public inline fun NonEmptyList.unzip(f: (C) -> Pair): Pair, NonEmptyList> { + contract { callsInPlace(f, InvocationKind.AT_LEAST_ONCE) } + return map { f(it) }.stdlibUnzip().let { (l1, l2) -> l1.toNonEmptyListOrNull()!! to l2.toNonEmptyListOrNull()!! } +} public inline fun NonEmptyList.mapOrAccumulate( combine: (E, E) -> E, diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Option.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Option.kt index 70b88526a0f..72f396cf964 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Option.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Option.kt @@ -585,14 +585,17 @@ public fun Option>.flatten(): Option = public fun Option>.toMap(): Map = this.toList().toMap() -public fun Option.combine(other: Option, combine: (A, A) -> A): Option = - when (this) { +public inline fun Option.combine(other: Option, combine: (A, A) -> A): Option { + contract { callsInPlace(combine, InvocationKind.AT_MOST_ONCE) } + return when (this) { is Some -> when (other) { is Some -> Some(combine(value, other.value)) None -> this } + None -> other } +} public operator fun > Option.compareTo(other: Option): Int = fold( { other.fold({ 0 }, { -1 }) }, @@ -645,8 +648,10 @@ public operator fun > Option.compareTo(other: Option): I * * */ -public inline fun Option.recover(recover: SingletonRaise.() -> A): Option = - when (this@recover) { +public inline fun Option.recover(recover: SingletonRaise.() -> A): Option { + contract { callsInPlace(recover, InvocationKind.AT_MOST_ONCE) } + return when (this@recover) { is None -> option { recover() } is Some -> this@recover } +} diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt index e72c48aa2d4..f2e834bdad8 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Builders.kt @@ -246,7 +246,7 @@ public class SingletonRaise(private val raise: Raise): Raise { public inline fun ignoreErrors( block: SingletonRaise.() -> A, ): A { - contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } // This is safe because SingletonRaise never leaks the e from `raise(e: E)`, instead always calling `raise()`. // and hence the type parameter of SingletonRaise merely states what errors it accepts and ignores. @Suppress("UNCHECKED_CAST") @@ -285,10 +285,16 @@ public class ResultRaise(private val raise: Raise) : Raise public inline fun recover( @BuilderInference block: ResultRaise.() -> A, recover: (Throwable) -> A, - ): A = result(block).fold( - onSuccess = { it }, - onFailure = { recover(it) } - ) + ): A { + contract { + callsInPlace(block, InvocationKind.AT_MOST_ONCE) + callsInPlace(recover, InvocationKind.AT_MOST_ONCE) + } + return result(block).fold( + onSuccess = { it }, + onFailure = { recover(it) } + ) + } } /** diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/ErrorHandlers.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/ErrorHandlers.kt index fa49119e799..edbdc6931e6 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/ErrorHandlers.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/ErrorHandlers.kt @@ -1,9 +1,12 @@ @file:JvmMultifileClass @file:JvmName("RaiseKt") -@file:OptIn(ExperimentalTypeInference::class) +@file:OptIn(ExperimentalTypeInference::class, ExperimentalContracts::class) package arrow.core.raise import arrow.core.nonFatalOrThrow +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.experimental.ExperimentalTypeInference import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName @@ -93,8 +96,10 @@ public fun Effect.catch(): Effect> = catch({ Result.success(invoke()) }, Result.Companion::failure) } -public suspend inline infix fun Effect.getOrElse(recover: (error: Error) -> A): A = - recover({ invoke() }) { recover(it) } +public suspend inline infix fun Effect.getOrElse(recover: (error: Error) -> A): A { + contract { callsInPlace(recover, InvocationKind.AT_MOST_ONCE) } + return recover({ invoke() }) { recover(it) } +} /** * Transform the raised value [Error] of the `Effect` into [OtherError], @@ -131,8 +136,10 @@ public inline infix fun EagerEffect. ): EagerEffect = eagerEffect { catch({ invoke() }) { t: T -> catch(t) } } -public inline infix fun EagerEffect.getOrElse(recover: (error: Error) -> A): A = - recover({ invoke() }, recover) +public inline infix fun EagerEffect.getOrElse(recover: (error: Error) -> A): A { + contract { callsInPlace(recover, InvocationKind.AT_MOST_ONCE) } + return recover({ invoke() }, recover) +} /** * Transform the raised value [Error] of the `EagerEffect` into [OtherError]. diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt index d7fd73054c1..b97098f26e8 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/Raise.kt @@ -14,6 +14,7 @@ import arrow.core.recover import kotlin.coroutines.cancellation.CancellationException import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind.AT_MOST_ONCE +import kotlin.contracts.InvocationKind.EXACTLY_ONCE import kotlin.contracts.contract import kotlin.experimental.ExperimentalTypeInference import kotlin.jvm.JvmMultifileClass @@ -656,10 +657,9 @@ public inline fun Raise.withError( @BuilderInference block: Raise.() -> A ): A { contract { - callsInPlace(transform, AT_MOST_ONCE) - callsInPlace(block, AT_MOST_ONCE) + callsInPlace(block, EXACTLY_ONCE) } - return recover(block) { raise(transform(it)) } + recover({ return block(this) }) { raise(transform(it)) } } /** diff --git a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt index 61f9936000d..cba76cf5acf 100644 --- a/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt +++ b/arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/raise/RaiseAccumulate.kt @@ -11,6 +11,7 @@ import arrow.core.collectionSizeOrDefault import arrow.core.toNonEmptyListOrNull import arrow.core.toNonEmptySetOrNull import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind.AT_LEAST_ONCE import kotlin.contracts.InvocationKind.AT_MOST_ONCE import kotlin.contracts.InvocationKind.EXACTLY_ONCE import kotlin.contracts.contract @@ -34,7 +35,11 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action2: RaiseAccumulate.() -> B, block: (A, B) -> C ): C { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( combine, action1, @@ -59,7 +64,12 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action3: RaiseAccumulate.() -> C, block: (A, B, C) -> D ): D { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( combine, action1, @@ -86,7 +96,13 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action4: RaiseAccumulate.() -> D, block: (A, B, C, D) -> E ): E { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( combine, action1, @@ -115,7 +131,14 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action5: RaiseAccumulate.() -> E, block: (A, B, C, D, E) -> F ): F { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( combine, action1, @@ -146,7 +169,15 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action6: RaiseAccumulate.() -> F, block: (A, B, C, D, E, F) -> G ): G { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( combine, action1, @@ -179,7 +210,16 @@ public inline fun Raise.zipOrAccumulate( @BuilderInference action7: RaiseAccumulate.() -> G, block: (A, B, C, D, E, F, G) -> H ): H { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(action7, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( combine, action1, @@ -214,7 +254,17 @@ public inline fun Raise.zipOrAccumulat @BuilderInference action8: RaiseAccumulate.() -> H, block: (A, B, C, D, E, F, G, H) -> I ): I { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(action7, EXACTLY_ONCE) + callsInPlace(action8, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( combine, action1, @@ -251,7 +301,18 @@ public inline fun Raise.zipOrAccumu @BuilderInference action9: RaiseAccumulate.() -> I, block: (A, B, C, D, E, F, G, H, I) -> J ): J { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(action7, EXACTLY_ONCE) + callsInPlace(action8, EXACTLY_ONCE) + callsInPlace(action9, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return withError({ it.reduce(combine) }) { zipOrAccumulate(action1, action2, action3, action4, action5, action6, action7, action8, action9, block) } @@ -270,7 +331,11 @@ public inline fun Raise>.zipOrAccumulate( @BuilderInference action2: RaiseAccumulate.() -> B, block: (A, B) -> C ): C { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( action1, action2, @@ -293,7 +358,12 @@ public inline fun Raise>.zipOrAccumulate @BuilderInference action3: RaiseAccumulate.() -> C, block: (A, B, C) -> D ): D { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( action1, action2, @@ -318,7 +388,13 @@ public inline fun Raise>.zipOrAccumul @BuilderInference action4: RaiseAccumulate.() -> D, block: (A, B, C, D) -> E ): E { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( action1, action2, @@ -345,7 +421,14 @@ public inline fun Raise>.zipOrAccu @BuilderInference action5: RaiseAccumulate.() -> E, block: (A, B, C, D, E) -> F ): F { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( action1, action2, @@ -374,7 +457,15 @@ public inline fun Raise>.zipOrA @BuilderInference action6: RaiseAccumulate.() -> F, block: (A, B, C, D, E, F) -> G ): G { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( action1, action2, @@ -405,7 +496,16 @@ public inline fun Raise>.zip @BuilderInference action7: RaiseAccumulate.() -> G, block: (A, B, C, D, E, F, G) -> H ): H { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(action7, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( action1, action2, @@ -438,7 +538,17 @@ public inline fun Raise>. @BuilderInference action8: RaiseAccumulate.() -> H, block: (A, B, C, D, E, F, G, H) -> I ): I { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(action7, EXACTLY_ONCE) + callsInPlace(action8, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return zipOrAccumulate( action1, action2, @@ -461,6 +571,7 @@ public inline fun Raise>. * and how to use it in [validation](https://arrow-kt.io/learn/typed-errors/validation/). */ @RaiseDSL @OptIn(ExperimentalRaiseAccumulateApi::class) +@Suppress("WRONG_INVOCATION_KIND") public inline fun Raise>.zipOrAccumulate( @BuilderInference action1: RaiseAccumulate.() -> A, @BuilderInference action2: RaiseAccumulate.() -> B, @@ -473,17 +584,28 @@ public inline fun Raise.() -> I, block: (A, B, C, D, E, F, G, H, I) -> J ): J { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(action1, EXACTLY_ONCE) + callsInPlace(action2, EXACTLY_ONCE) + callsInPlace(action3, EXACTLY_ONCE) + callsInPlace(action4, EXACTLY_ONCE) + callsInPlace(action5, EXACTLY_ONCE) + callsInPlace(action6, EXACTLY_ONCE) + callsInPlace(action7, EXACTLY_ONCE) + callsInPlace(action8, EXACTLY_ONCE) + callsInPlace(action9, EXACTLY_ONCE) + callsInPlace(block, EXACTLY_ONCE) + } return accumulate { - val a = accumulating(action1) - val b = accumulating(action2) - val c = accumulating(action3) - val d = accumulating(action4) - val e = accumulating(action5) - val f = accumulating(action6) - val g = accumulating(action7) - val h = accumulating(action8) - val i = accumulating(action9) + val a = accumulating { action1() } + val b = accumulating { action2() } + val c = accumulating { action3() } + val d = accumulating { action4() } + val e = accumulating { action5() } + val f = accumulating { action6() } + val g = accumulating { action7() } + val h = accumulating { action8() } + val i = accumulating { action9() } block(a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value, i.value) } } @@ -628,10 +750,16 @@ public inline fun Raise>.mapOrAccumulate( * and how to use it in [validation](https://arrow-kt.io/learn/typed-errors/validation/). */ @RaiseDSL +@Suppress("WRONG_INVOCATION_KIND") public inline fun Raise>.mapOrAccumulate( nonEmptyList: NonEmptyList, @BuilderInference transform: RaiseAccumulate.(A) -> B -): NonEmptyList = requireNotNull(mapOrAccumulate(nonEmptyList.all, transform).toNonEmptyListOrNull()) +): NonEmptyList { + // For a NonEmptyList to be returned, there must be a B, which can only be produced by transform + // thus transform must be called at least once (or alternatively an error is raised or an exception is thrown etc) + contract { callsInPlace(transform, AT_LEAST_ONCE) } + return requireNotNull(mapOrAccumulate(nonEmptyList.all) { transform(it) }.toNonEmptyListOrNull()) +} /** * Accumulate the errors obtained by executing the [transform] over every element of [NonEmptySet]. @@ -641,14 +769,18 @@ public inline fun Raise>.mapOrAccumulate( * and how to use it in [validation](https://arrow-kt.io/learn/typed-errors/validation/). */ @RaiseDSL +@Suppress("WRONG_INVOCATION_KIND") public inline fun Raise>.mapOrAccumulate( nonEmptySet: NonEmptySet, @BuilderInference transform: RaiseAccumulate.(A) -> B -): NonEmptySet = buildSet(nonEmptySet.size) { - forEachAccumulatingImpl(nonEmptySet.iterator()) { item, hasErrors -> - transform(item).also { if (!hasErrors) add(it) } - } -}.toNonEmptySetOrNull()!! +): NonEmptySet { + contract { callsInPlace(transform, AT_LEAST_ONCE) } + return buildSet(nonEmptySet.size) { + forEachAccumulatingImpl(nonEmptySet.iterator()) { item, hasErrors -> + transform(item).also { if (!hasErrors) add(it) } + } + }.toNonEmptySetOrNull()!! +} @RaiseDSL @Deprecated( @@ -713,7 +845,9 @@ public inline fun accumulate( raise: (Raise>.() -> A) -> R, crossinline block: RaiseAccumulate.() -> A ): R { - contract { callsInPlace(block, AT_MOST_ONCE) } + contract { + callsInPlace(raise, EXACTLY_ONCE) + } return raise { accumulate(block) } } @@ -743,12 +877,18 @@ public open class RaiseAccumulate( @RaiseDSL public inline fun NonEmptyList.mapOrAccumulate( transform: RaiseAccumulate.(A) -> B - ): NonEmptyList = raise.mapOrAccumulate(this, transform) + ): NonEmptyList { + contract { callsInPlace(transform, AT_LEAST_ONCE) } + return raise.mapOrAccumulate(this, transform) + } @RaiseDSL public inline fun NonEmptySet.mapOrAccumulate( transform: RaiseAccumulate.(A) -> B - ): NonEmptySet = raise.mapOrAccumulate(this, transform) + ): NonEmptySet { + contract { callsInPlace(transform, AT_LEAST_ONCE) } + return raise.mapOrAccumulate(this, transform) + } @RaiseDSL public inline fun Map.mapOrAccumulate( @@ -772,14 +912,20 @@ public open class RaiseAccumulate( public inline fun mapOrAccumulate( list: NonEmptyList, transform: RaiseAccumulate.(A) -> B - ): NonEmptyList = raise.mapOrAccumulate(list, transform) + ): NonEmptyList { + contract { callsInPlace(transform, AT_LEAST_ONCE) } + return raise.mapOrAccumulate(list, transform) + } @RaiseDSL @JvmName("_mapOrAccumulate") public inline fun mapOrAccumulate( set: NonEmptySet, transform: RaiseAccumulate.(A) -> B - ): NonEmptySet = raise.mapOrAccumulate(set, transform) + ): NonEmptySet { + contract { callsInPlace(transform, AT_LEAST_ONCE) } + return raise.mapOrAccumulate(set, transform) + } @RaiseDSL override fun Iterable>.bindAll(): List = @@ -822,24 +968,27 @@ public open class RaiseAccumulate( accumulating { this@bindNelOrAccumulate.bindNel() } @ExperimentalRaiseAccumulateApi - public fun ensureOrAccumulate(condition: Boolean, raise: () -> Error) { + public inline fun ensureOrAccumulate(condition: Boolean, raise: () -> Error) { + contract { callsInPlace(raise, AT_MOST_ONCE) } accumulating { ensure(condition, raise) } } @ExperimentalRaiseAccumulateApi - public fun ensureNotNullOrAccumulate(value: B?, raise: () -> Error) { - contract { returns() implies (value != null) } + public inline fun ensureNotNullOrAccumulate(value: B?, raise: () -> Error) { + contract { callsInPlace(raise, AT_MOST_ONCE) } ensureOrAccumulate(value != null, raise) } @ExperimentalRaiseAccumulateApi - public inline fun accumulating(block: RaiseAccumulate.() -> A): Value = - recover(inner@{ + public inline fun accumulating(block: RaiseAccumulate.() -> A): Value { + contract { callsInPlace(block, AT_MOST_ONCE) } + return recover(inner@{ Ok(block(RaiseAccumulate(this@inner))) }) { addErrors(it) Error() } + } public inline operator fun Value.getValue(thisRef: Nothing?, property: KProperty<*>): A = value diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Bracket.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Bracket.kt index 0a98b60a085..2c2e1afa865 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Bracket.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Bracket.kt @@ -1,9 +1,14 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.fx.coroutines import arrow.core.nonFatalOrThrow import kotlinx.coroutines.CancellationException import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.withContext +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract public sealed class ExitCase { public object Completed : ExitCase() { @@ -43,10 +48,16 @@ internal val ExitCase.errorOrNull: Throwable? public suspend inline fun onCancel( fa: suspend () -> A, crossinline onCancel: suspend () -> Unit -): A = guaranteeCase(fa) { case -> - when (case) { - is ExitCase.Cancelled -> onCancel.invoke() - else -> Unit +): A { + contract { + callsInPlace(fa, InvocationKind.EXACTLY_ONCE) + callsInPlace(onCancel, InvocationKind.AT_MOST_ONCE) + } + return guaranteeCase(fa) { case -> + when (case) { + is ExitCase.Cancelled -> onCancel() + else -> Unit + } } } @@ -67,7 +78,13 @@ public suspend inline fun onCancel( public suspend inline fun guarantee( fa: suspend () -> A, crossinline finalizer: suspend () -> Unit -): A = guaranteeCase(fa) { finalizer() } +): A { + contract { + callsInPlace(fa, InvocationKind.EXACTLY_ONCE) + callsInPlace(finalizer, InvocationKind.EXACTLY_ONCE) + } + return guaranteeCase(fa) { finalizer() } +} /** * Guarantees execution of a given [finalizer] after [fa] regardless of success, error or cancellation, allowing @@ -87,17 +104,19 @@ public suspend inline fun guarantee( public suspend inline fun guaranteeCase( fa: suspend () -> A, crossinline finalizer: suspend (ExitCase) -> Unit -): A = finalizeCase({ fa() }) { ex -> - try { - withContext(NonCancellable) { - finalizer(ex) - } - } catch (e: Throwable) { - e.nonFatalOrThrow() - when (ex) { - ExitCase.Completed -> throw e - is ExitCase.Failure -> ex.failure.addSuppressed(e) - is ExitCase.Cancelled -> ex.exception.addSuppressed(e) +): A { + contract { + callsInPlace(fa, InvocationKind.EXACTLY_ONCE) + callsInPlace(finalizer, InvocationKind.EXACTLY_ONCE) + } + return finalizeCase({ fa() }) { ex -> + try { + withContext(NonCancellable) { + finalizer(ex) + } + } catch (e: Throwable) { + e.nonFatalOrThrow() + throw ex.errorOrNull?.also { it.addSuppressed(e) } ?: e } } } @@ -147,7 +166,14 @@ public suspend inline fun bracket( crossinline acquire: suspend () -> A, use: suspend (A) -> B, crossinline release: suspend (A) -> Unit -): B = bracketCase(acquire, use) { acquired, _ -> release(acquired) } +): B { + contract { + callsInPlace(acquire, InvocationKind.EXACTLY_ONCE) + callsInPlace(use, InvocationKind.EXACTLY_ONCE) + callsInPlace(release, InvocationKind.EXACTLY_ONCE) + } + return bracketCase(acquire, use) { acquired, _ -> release(acquired) } +} /** * A way to safely acquire a resource and release in the face of errors and cancellation. @@ -217,12 +243,21 @@ public suspend inline fun bracketCase( use: suspend (A) -> B, crossinline release: suspend (A, ExitCase) -> Unit ): B { + contract { + callsInPlace(acquire, InvocationKind.EXACTLY_ONCE) + callsInPlace(use, InvocationKind.EXACTLY_ONCE) + callsInPlace(release, InvocationKind.EXACTLY_ONCE) + } val acquired = withContext(NonCancellable) { acquire() } return guaranteeCase({ use(acquired) }) { release(acquired, it) } } @PublishedApi internal inline fun finalizeCase(block: () -> R, finalizer: (ExitCase) -> Unit): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + callsInPlace(finalizer, InvocationKind.EXACTLY_ONCE) + } var exitCase: ExitCase = ExitCase.Completed return try { block() diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZip.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZip.kt index 46a86a257bc..3cf04d34a3d 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZip.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZip.kt @@ -1,4 +1,5 @@ @file:Suppress("UNCHECKED_CAST") +@file:OptIn(ExperimentalContracts::class) package arrow.fx.coroutines @@ -10,6 +11,9 @@ import kotlin.coroutines.ContinuationInterceptor import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.awaitAll +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract /** * Runs [fa], [fb] in parallel on [Dispatchers.Default] and combines their results using the provided function. @@ -41,7 +45,14 @@ public suspend inline fun parZip( crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B, crossinline f: suspend CoroutineScope.(A, B) -> C -): C = parZip(Dispatchers.Default, fa, fb, f) +): C { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, f) +} /** * Runs [fa], [fb] in parallel on [ctx] and combines their results using the provided function. @@ -75,16 +86,24 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B, crossinline f: suspend CoroutineScope.(A, B) -> C -): C = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val (a, b) = awaitAll(faa, fbb) - f(a as A, b as B) +): C { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val (a, b) = awaitAll(faa, fbb) + f(a as A, b as B) + } } /** @@ -120,7 +139,15 @@ public suspend inline fun parZip( crossinline fb: suspend CoroutineScope.() -> B, crossinline fc: suspend CoroutineScope.() -> C, crossinline f: suspend CoroutineScope.(A, B, C) -> D -): D = parZip(Dispatchers.Default, fa, fb, fc, f) +): D { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, fc, f) +} /** * Runs [fa], [fb], [fc] in parallel on [ctx] and combines their results using the provided function. @@ -156,18 +183,27 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B, crossinline fc: suspend CoroutineScope.() -> C, crossinline f: suspend CoroutineScope.(A, B, C) -> D -): D = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val fcc = async(ctx) { fc() } - val (a, b, c) = awaitAll(faa, fbb, fcc) - f(a as A, b as B, c as C) +): D { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val fcc = async(ctx) { fc() } + val (a, b, c) = awaitAll(faa, fbb, fcc) + f(a as A, b as B, c as C) + } } /** @@ -206,7 +242,16 @@ public suspend inline fun parZip( crossinline fc: suspend CoroutineScope.() -> C, crossinline fd: suspend CoroutineScope.() -> D, crossinline f: suspend CoroutineScope.(A, B, C, D) -> E -): E = parZip(Dispatchers.Default, fa, fb, fc, fd, f) +): E { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, fc, fd, f) +} /** * Runs [fa], [fb], [fc], [fd] in parallel on [ctx] and combines their results using the provided function. @@ -245,6 +290,7 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, @@ -252,13 +298,22 @@ public suspend inline fun parZip( crossinline fc: suspend CoroutineScope.() -> C, crossinline fd: suspend CoroutineScope.() -> D, crossinline f: suspend CoroutineScope.(A, B, C, D) -> E -): E = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val fcc = async(ctx) { fc() } - val fdd = async(ctx) { fd() } - val (a, b, c, d) = awaitAll(faa, fbb, fcc, fdd) - f(a as A, b as B, c as C, d as D) +): E { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val fcc = async(ctx) { fc() } + val fdd = async(ctx) { fd() } + val (a, b, c, d) = awaitAll(faa, fbb, fcc, fdd) + f(a as A, b as B, c as C, d as D) + } } /** @@ -301,7 +356,17 @@ public suspend inline fun parZip( crossinline fd: suspend CoroutineScope.() -> D, crossinline fe: suspend CoroutineScope.() -> E, crossinline f: suspend CoroutineScope.(A, B, C, D, E) -> F -): F = parZip(Dispatchers.Default, fa, fb, fc, fd, fe, f) +): F { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, fc, fd, fe, f) +} /** * Runs [fa], [fb], [fc], [fd], [fe] in parallel on [ctx] and combines their results using the provided function. @@ -342,6 +407,7 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, @@ -350,14 +416,24 @@ public suspend inline fun parZip( crossinline fd: suspend CoroutineScope.() -> D, crossinline fe: suspend CoroutineScope.() -> E, crossinline f: suspend CoroutineScope.(A, B, C, D, E) -> F -): F = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val fcc = async(ctx) { fc() } - val fdd = async(ctx) { fd() } - val fee = async(ctx) { fe() } - val (a, b, c, d, e) = awaitAll(faa, fbb, fcc, fdd, fee) - f(a as A, b as B, c as C, d as D, e as E) +): F { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val fcc = async(ctx) { fc() } + val fdd = async(ctx) { fd() } + val fee = async(ctx) { fe() } + val (a, b, c, d, e) = awaitAll(faa, fbb, fcc, fdd, fee) + f(a as A, b as B, c as C, d as D, e as E) + } } /** @@ -403,7 +479,18 @@ public suspend inline fun parZip( crossinline fe: suspend CoroutineScope.() -> E, crossinline ff: suspend CoroutineScope.() -> F, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F) -> G -): G = parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, f) +): G { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, f) +} /** * Runs [fa], [fb], [fc], [fd], [fe], [ff] in parallel on [ctx] and combines their results using the provided function. @@ -446,6 +533,7 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, @@ -455,15 +543,26 @@ public suspend inline fun parZip( crossinline fe: suspend CoroutineScope.() -> E, crossinline ff: suspend CoroutineScope.() -> F, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F) -> G -): G = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val fcc = async(ctx) { fc() } - val fdd = async(ctx) { fd() } - val fee = async(ctx) { fe() } - val fgg = async(ctx) { ff() } - val res = awaitAll(faa, fbb, fcc, fdd, fee, fgg) - f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F) +): G { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val fcc = async(ctx) { fc() } + val fdd = async(ctx) { fd() } + val fee = async(ctx) { fe() } + val fgg = async(ctx) { ff() } + val res = awaitAll(faa, fbb, fcc, fdd, fee, fgg) + f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F) + } } /** @@ -512,7 +611,19 @@ public suspend inline fun parZip( crossinline ff: suspend CoroutineScope.() -> F, crossinline fg: suspend CoroutineScope.() -> G, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F, G) -> H -): H = parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, fg, f) +): H { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, fg, f) +} /** * Runs [fa], [fb], [fc], [fd], [fe], [ff], [fg] in parallel on [ctx] and combines their results using the provided function. @@ -557,6 +668,7 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, @@ -567,16 +679,28 @@ public suspend inline fun parZip( crossinline ff: suspend CoroutineScope.() -> F, crossinline fg: suspend CoroutineScope.() -> G, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F, G) -> H -): H = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val fcc = async(ctx) { fc() } - val fdd = async(ctx) { fd() } - val fee = async(ctx) { fe() } - val fDef = async(ctx) { ff() } - val fgg = async(ctx) { fg() } - val res = awaitAll(faa, fbb, fcc, fdd, fee, fDef, fgg) - f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F, res[6] as G) +): H { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val fcc = async(ctx) { fc() } + val fdd = async(ctx) { fd() } + val fee = async(ctx) { fe() } + val fDef = async(ctx) { ff() } + val fgg = async(ctx) { fg() } + val res = awaitAll(faa, fbb, fcc, fdd, fee, fDef, fgg) + f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F, res[6] as G) + } } /** @@ -628,7 +752,20 @@ public suspend inline fun parZip( crossinline fg: suspend CoroutineScope.() -> G, crossinline fh: suspend CoroutineScope.() -> H, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F, G, H) -> I -): I = parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, fg, fh, f) +): I { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, fg, fh, f) +} /** * Runs [fa], [fb], [fc], [fd], [fe], [ff], [fg], [fh] in parallel on [ctx] and combines their results using the provided function. @@ -675,6 +812,7 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, @@ -686,17 +824,30 @@ public suspend inline fun parZip( crossinline fg: suspend CoroutineScope.() -> G, crossinline fh: suspend CoroutineScope.() -> H, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F, G, H) -> I -): I = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val fcc = async(ctx) { fc() } - val fdd = async(ctx) { fd() } - val fee = async(ctx) { fe() } - val fDef = async(ctx) { ff() } - val fgg = async(ctx) { fg() } - val fhh = async(ctx) { fh() } - val res = awaitAll(faa, fbb, fcc, fdd, fee, fDef, fgg, fhh) - f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F, res[6] as G, res[7] as H) +): I { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val fcc = async(ctx) { fc() } + val fdd = async(ctx) { fd() } + val fee = async(ctx) { fe() } + val fDef = async(ctx) { ff() } + val fgg = async(ctx) { fg() } + val fhh = async(ctx) { fh() } + val res = awaitAll(faa, fbb, fcc, fdd, fee, fDef, fgg, fhh) + f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F, res[6] as G, res[7] as H) + } } /** @@ -751,7 +902,21 @@ public suspend inline fun parZip( crossinline fh: suspend CoroutineScope.() -> H, crossinline fi: suspend CoroutineScope.() -> I, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F, G, H, I) -> J -): J = parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, fg, fh, fi, f) +): J { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return parZip(Dispatchers.Default, fa, fb, fc, fd, fe, ff, fg, fh, fi, f) +} /** * Runs [fa], [fb], [fc], [fd], [fe], [ff], [fg], [fh], [fi] in parallel on [ctx] and combines their results using the provided function. @@ -800,6 +965,7 @@ public suspend inline fun parZip( * * @see parZip for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun parZip( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, @@ -812,16 +978,30 @@ public suspend inline fun parZip( crossinline fh: suspend CoroutineScope.() -> H, crossinline fi: suspend CoroutineScope.() -> I, crossinline f: suspend CoroutineScope.(A, B, C, D, E, F, G, H, I) -> J -): J = coroutineScope { - val faa = async(ctx) { fa() } - val fbb = async(ctx) { fb() } - val fcc = async(ctx) { fc() } - val fdd = async(ctx) { fd() } - val fee = async(ctx) { fe() } - val fDef = async(ctx) { ff() } - val fgg = async(ctx) { fg() } - val fhh = async(ctx) { fh() } - val fii = async(ctx) { fi() } - val res = awaitAll(faa, fbb, fcc, fdd, fee, fDef, fgg, fhh, fii) - f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F, res[6] as G, res[7] as H, res[8] as I) +): J { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(fe, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { + val faa = async(ctx) { fa() } + val fbb = async(ctx) { fb() } + val fcc = async(ctx) { fc() } + val fdd = async(ctx) { fd() } + val fee = async(ctx) { fe() } + val fDef = async(ctx) { ff() } + val fgg = async(ctx) { fg() } + val fhh = async(ctx) { fh() } + val fii = async(ctx) { fi() } + val res = awaitAll(faa, fbb, fcc, fdd, fee, fDef, fgg, fhh, fii) + f(res[0] as A, res[1] as B, res[2] as C, res[3] as D, res[4] as E, res[5] as F, res[6] as G, res[7] as H, res[8] as I) + } } diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZipOrAccumulate.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZipOrAccumulate.kt index 3a7ea2104b7..ecc03a769c9 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZipOrAccumulate.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/ParZipOrAccumulate.kt @@ -1,13 +1,17 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.fx.coroutines -import arrow.core.Either import arrow.core.NonEmptyList -import arrow.core.getOrElse import arrow.core.raise.Raise import arrow.core.raise.either +import arrow.core.raise.zipOrAccumulate import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlinx.coroutines.CoroutineScope +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract //region 2-arity public suspend inline fun Raise.parZipOrAccumulate( @@ -15,8 +19,14 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline transform: suspend CoroutineScope.(A, B) -> C -): C = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, transform) +): C { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -24,35 +34,53 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline transform: suspend CoroutineScope.(A, B) -> C -): C = - parZip( +): C { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b -> - Either.zipOrAccumulate(a, b) { aa, bb -> transform(aa, bb) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }) { aa, bb -> transform(aa, bb) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline transform: suspend CoroutineScope.(A, B) -> C -): C = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, transform) +): C { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, crossinline fa: suspend ScopedRaiseAccumulate.() -> A, crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline transform: suspend CoroutineScope.(A, B) -> C -): C = - parZip( +): C { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b -> - Either.zipOrAccumulate(a, b) { aa, bb -> transform(aa, bb) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }) { aa, bb -> transform(aa, bb) } } +} //endregion //region 3-arity @@ -62,8 +90,15 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline transform: suspend CoroutineScope.(A, B, C) -> D -): D = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, transform) +): D { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -72,23 +107,39 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline transform: suspend CoroutineScope.(A, B, C) -> D -): D = - parZip( +): D { + contract { + // Contract is valid because for D to be returned, transform must be called + // with A, B, C, and hence fa, fb, fc must be called. + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fc(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c -> - Either.zipOrAccumulate(a, b, c) { aa, bb, cc -> transform(aa, bb, cc) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }, { c.bindNel() }) { aa, bb, cc -> transform(aa, bb, cc) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline transform: suspend CoroutineScope.(A, B, C) -> D -): D = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, transform) +): D { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, @@ -96,15 +147,22 @@ public suspend inline fun Raise>.parZipOrAccumul crossinline fb: suspend ScopedRaiseAccumulate.() -> B, crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline transform: suspend CoroutineScope.(A, B, C) -> D -): D = - parZip( +): D { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fc(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c -> - Either.zipOrAccumulate(a, b, c) { aa, bb, cc -> transform(aa, bb, cc) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }, { c.bindNel() }) { aa, bb, cc -> transform(aa, bb, cc) } } +} //endregion //region 4-arity @@ -115,8 +173,16 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline transform: suspend CoroutineScope.(A, B, C, D) -> F -): F = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, transform) +): F { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -126,16 +192,24 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline transform: suspend CoroutineScope.(A, B, C, D) -> F -): F = - parZip( +): F { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fc(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fd(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d -> - Either.zipOrAccumulate(a, b, c, d) { aa, bb, cc, dd -> transform(aa, bb, cc, dd) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }) { aa, bb, cc, dd -> transform(aa, bb, cc, dd) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, @@ -143,8 +217,16 @@ public suspend inline fun Raise>.parZipOrAccu crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline transform: suspend CoroutineScope.(A, B, C, D) -> F -): F = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, transform) +): F { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, @@ -153,16 +235,24 @@ public suspend inline fun Raise>.parZipOrAccu crossinline fc: suspend ScopedRaiseAccumulate.() -> C, crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline transform: suspend CoroutineScope.(A, B, C, D) -> F -): F = - parZip( +): F { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fc(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fd(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d -> - Either.zipOrAccumulate(a, b, c, d) { aa, bb, cc, dd -> transform(aa, bb, cc, dd) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }) { aa, bb, cc, dd -> transform(aa, bb, cc, dd) } } +} //endregion //region 5-arity @@ -174,8 +264,17 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline transform: suspend CoroutineScope.(A, B, C, D, F) -> G -): G = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, transform) +): G { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -186,8 +285,16 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline transform: suspend CoroutineScope.(A, B, C, D, F) -> G -): G = - parZip( +): G { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -195,8 +302,9 @@ public suspend inline fun Raise.parZipOrAccumulate( { either { fd(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { ff(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f -> - Either.zipOrAccumulate(a, b, c, d, f) { aa, bb, cc, dd, ff -> transform(aa, bb, cc, dd, ff) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }) { aa, bb, cc, dd, ff -> transform(aa, bb, cc, dd, ff) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, @@ -205,8 +313,17 @@ public suspend inline fun Raise>.parZipOrA crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline transform: suspend CoroutineScope.(A, B, C, D, F) -> G -): G = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, transform) +): G { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, @@ -216,8 +333,16 @@ public suspend inline fun Raise>.parZipOrA crossinline fd: suspend ScopedRaiseAccumulate.() -> D, crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline transform: suspend CoroutineScope.(A, B, C, D, F) -> G -): G = - parZip( +): G { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -225,8 +350,9 @@ public suspend inline fun Raise>.parZipOrA { either { fd(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { ff(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f -> - Either.zipOrAccumulate(a, b, c, d, f) { aa, bb, cc, dd, ff -> transform(aa, bb, cc, dd, ff) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }) { aa, bb, cc, dd, ff -> transform(aa, bb, cc, dd, ff) } } +} //endregion //region 6-arity @@ -239,8 +365,18 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G) -> H -): H = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, transform) +): H { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -252,8 +388,17 @@ public suspend inline fun Raise.parZipOrAccumulate( crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G) -> H -): H = - parZip( +): H { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -262,8 +407,9 @@ public suspend inline fun Raise.parZipOrAccumulate( { either { ff(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fg(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f, g -> - Either.zipOrAccumulate(a, b, c, d, f, g) { aa, bb, cc, dd, ff, gg -> transform(aa, bb, cc, dd, ff, gg) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }) { aa, bb, cc, dd, ff, gg -> transform(aa, bb, cc, dd, ff, gg) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, @@ -273,8 +419,18 @@ public suspend inline fun Raise>.parZip crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G) -> H -): H = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, transform) +): H { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, @@ -285,8 +441,17 @@ public suspend inline fun Raise>.parZip crossinline ff: suspend ScopedRaiseAccumulate.() -> F, crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G) -> H -): H = - parZip( +): H { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -295,8 +460,9 @@ public suspend inline fun Raise>.parZip { either { ff(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fg(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f, g -> - Either.zipOrAccumulate(a, b, c, d, f, g) { aa, bb, cc, dd, ff, gg -> transform(aa, bb, cc, dd, ff, gg) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }) { aa, bb, cc, dd, ff, gg -> transform(aa, bb, cc, dd, ff, gg) } } +} //endregion //region 7-arity @@ -310,8 +476,19 @@ public suspend inline fun Raise.parZipOrAccumulat crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H) -> I -): I = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, fh, transform) +): I { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, fh, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -324,8 +501,18 @@ public suspend inline fun Raise.parZipOrAccumulat crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H) -> I -): I = - parZip( +): I { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -335,8 +522,9 @@ public suspend inline fun Raise.parZipOrAccumulat { either { fg(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fh(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f, g, h -> - Either.zipOrAccumulate(a, b, c, d, f, g, h) { aa, bb, cc, dd, ff, gg, hh -> transform(aa, bb, cc, dd, ff, gg, hh) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }, { h.bindNel() }) { aa, bb, cc, dd, ff, gg, hh -> transform(aa, bb, cc, dd, ff, gg, hh) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, @@ -347,8 +535,19 @@ public suspend inline fun Raise>.par crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H) -> I -): I = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, fh, transform) +): I { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, fh, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, @@ -360,8 +559,18 @@ public suspend inline fun Raise>.par crossinline fg: suspend ScopedRaiseAccumulate.() -> G, crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H) -> I -): I = - parZip( +): I { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -371,8 +580,9 @@ public suspend inline fun Raise>.par { either { fg(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fh(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f, g, h -> - Either.zipOrAccumulate(a, b, c, d, f, g, h) { aa, bb, cc, dd, ff, gg, hh -> transform(aa, bb, cc, dd, ff, gg, hh) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }, { h.bindNel() }) { aa, bb, cc, dd, ff, gg, hh -> transform(aa, bb, cc, dd, ff, gg, hh) } } +} //endregion //region 8-arity @@ -387,8 +597,20 @@ public suspend inline fun Raise.parZipOrAccumu crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline fi: suspend ScopedRaiseAccumulate.() -> I, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I) -> J -): J = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, fh, fi, transform) +): J { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, fh, fi, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -402,8 +624,19 @@ public suspend inline fun Raise.parZipOrAccumu crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline fi: suspend ScopedRaiseAccumulate.() -> I, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I) -> J -): J = - parZip( +): J { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -414,8 +647,9 @@ public suspend inline fun Raise.parZipOrAccumu { either { fh(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fi(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f, g, h, i -> - Either.zipOrAccumulate(a, b, c, d, f, g, h, i) { aa, bb, cc, dd, ff, gg, hh, ii -> transform(aa, bb, cc, dd, ff, gg, hh, ii) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }, { h.bindNel() }, { i.bindNel() }) { aa, bb, cc, dd, ff, gg, hh, ii -> transform(aa, bb, cc, dd, ff, gg, hh, ii) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, @@ -427,8 +661,20 @@ public suspend inline fun Raise>. crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline fi: suspend ScopedRaiseAccumulate.() -> I, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I) -> J -): J = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, fh, fi, transform) +): J { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, fh, fi, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, @@ -441,8 +687,19 @@ public suspend inline fun Raise>. crossinline fh: suspend ScopedRaiseAccumulate.() -> H, crossinline fi: suspend ScopedRaiseAccumulate.() -> I, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I) -> J -): J = - parZip( +): J { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -453,8 +710,9 @@ public suspend inline fun Raise>. { either { fh(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fi(ScopedRaiseAccumulate(this, this@parZip)) } }, ) { a, b, c, d, f, g, h, i -> - Either.zipOrAccumulate(a, b, c, d, f, g, h, i) { aa, bb, cc, dd, ff, gg, hh, ii -> transform(aa, bb, cc, dd, ff, gg, hh, ii) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }, { h.bindNel() }, { i.bindNel() }) { aa, bb, cc, dd, ff, gg, hh, ii -> transform(aa, bb, cc, dd, ff, gg, hh, ii) } } +} //endregion //region 9-arity @@ -470,8 +728,21 @@ public suspend inline fun Raise.parZipOrAcc crossinline fi: suspend ScopedRaiseAccumulate.() -> I, crossinline fj: suspend ScopedRaiseAccumulate.() -> J, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I, J) -> K -): K = - parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, fh, fi, fj, transform) +): K { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(fj, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, combine, fa, fb, fc, fd, ff, fg, fh, fi, fj, transform) +} public suspend inline fun Raise.parZipOrAccumulate( context: CoroutineContext, @@ -486,8 +757,20 @@ public suspend inline fun Raise.parZipOrAcc crossinline fi: suspend ScopedRaiseAccumulate.() -> I, crossinline fj: suspend ScopedRaiseAccumulate.() -> J, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I, J) -> K -): K = - parZip( +): K { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(fj, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -499,8 +782,9 @@ public suspend inline fun Raise.parZipOrAcc { either { fi(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fj(ScopedRaiseAccumulate(this, this@parZip)) } } ) { a, b, c, d, f, g, h, i, j -> - Either.zipOrAccumulate(a, b, c, d, f, g, h, i, j) { aa, bb, cc, dd, ff, gg, hh, ii, jj -> transform(aa, bb, cc, dd, ff, gg, hh, ii, jj) }.getOrElse { raise(it.reduce(combine)) } + zipOrAccumulate(combine, { a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }, { h.bindNel() }, { i.bindNel() }, { j.bindNel() }) { aa, bb, cc, dd, ff, gg, hh, ii, jj -> transform(aa, bb, cc, dd, ff, gg, hh, ii, jj) } } +} public suspend inline fun Raise>.parZipOrAccumulate( crossinline fa: suspend ScopedRaiseAccumulate.() -> A, @@ -513,8 +797,21 @@ public suspend inline fun Raise.() -> I, crossinline fj: suspend ScopedRaiseAccumulate.() -> J, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I, J) -> K -): K = - parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, fh, fi, fj, transform) +): K { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(fj, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZipOrAccumulate(EmptyCoroutineContext, fa, fb, fc, fd, ff, fg, fh, fi, fj, transform) +} public suspend inline fun Raise>.parZipOrAccumulate( context: CoroutineContext, @@ -528,8 +825,20 @@ public suspend inline fun Raise.() -> I, crossinline fj: suspend ScopedRaiseAccumulate.() -> J, crossinline transform: suspend CoroutineScope.(A, B, C, D, F, G, H, I, J) -> K -): K = - parZip( +): K { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + callsInPlace(fd, InvocationKind.AT_MOST_ONCE) + callsInPlace(ff, InvocationKind.AT_MOST_ONCE) + callsInPlace(fg, InvocationKind.AT_MOST_ONCE) + callsInPlace(fh, InvocationKind.AT_MOST_ONCE) + callsInPlace(fi, InvocationKind.AT_MOST_ONCE) + callsInPlace(fj, InvocationKind.AT_MOST_ONCE) + callsInPlace(transform, InvocationKind.EXACTLY_ONCE) + } + return parZip( context, { either { fa(ScopedRaiseAccumulate(this, this@parZip)) } }, { either { fb(ScopedRaiseAccumulate(this, this@parZip)) } }, @@ -541,6 +850,7 @@ public suspend inline fun Raise - Either.zipOrAccumulate(a, b, c, d, f, g, h, i, j) { aa, bb, cc, dd, ff, gg, hh, ii, jj -> transform(aa, bb, cc, dd, ff, gg, hh, ii, jj) }.bind() + zipOrAccumulate({ a.bindNel() }, { b.bindNel() }, { c.bindNel() }, { d.bindNel() }, { f.bindNel() }, { g.bindNel() }, { h.bindNel() }, { i.bindNel() }, { j.bindNel() }) { aa, bb, cc, dd, ff, gg, hh, ii, jj -> transform(aa, bb, cc, dd, ff, gg, hh, ii, jj) } } +} //endregion diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race2.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race2.kt index 003c405fdb3..d517acffafc 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race2.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race2.kt @@ -1,3 +1,5 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.fx.coroutines import arrow.core.Either @@ -7,6 +9,9 @@ import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.selects.select +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.coroutines.ContinuationInterceptor import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -46,8 +51,13 @@ import kotlin.coroutines.EmptyCoroutineContext * @see racePair for a version that does not automatically cancel the loser. * @see raceN for the same function that can race on any [CoroutineContext]. */ -public suspend inline fun raceN(crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B): Either = - raceN(Dispatchers.Default, fa, fb) +public suspend inline fun raceN(crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B): Either { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + } + return raceN(Dispatchers.Default, fa, fb) +} /** * Races the participants [fa], [fb] on the provided [CoroutineContext]. @@ -87,12 +97,17 @@ public suspend inline fun raceN(crossinline fa: suspend CoroutineScope.() * @return either [Either.Left] if [fa] won the race, or [Either.Right] if [fb] won the race. * @see raceN for a function that ensures it runs in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun raceN( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B -): Either = - coroutineScope { +): Either { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + } + return coroutineScope { val a = async(ctx) { fa() } val b = async(ctx) { fb() } select> { @@ -105,3 +120,4 @@ public suspend inline fun raceN( } } } +} diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race3.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race3.kt index 0838a6dcd70..7ab2d336670 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race3.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/Race3.kt @@ -1,3 +1,5 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.fx.coroutines import arrow.core.nonFatalOrThrow @@ -8,6 +10,9 @@ import kotlinx.coroutines.async import kotlinx.coroutines.cancelAndJoin import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.selects.select +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.coroutines.ContinuationInterceptor import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -21,10 +26,17 @@ public sealed class Race3 { ifA: (A) -> D, ifB: (B) -> D, ifC: (C) -> D - ): D = when (this) { - is First -> ifA(winner) - is Second -> ifB(winner) - is Third -> ifC(winner) + ): D { + contract { + callsInPlace(ifA, InvocationKind.AT_MOST_ONCE) + callsInPlace(ifB, InvocationKind.AT_MOST_ONCE) + callsInPlace(ifC, InvocationKind.AT_MOST_ONCE) + } + return when (this) { + is First -> ifA(winner) + is Second -> ifB(winner) + is Third -> ifC(winner) + } } } @@ -39,7 +51,14 @@ public suspend inline fun raceN( crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B, crossinline fc: suspend CoroutineScope.() -> C -): Race3 = raceN(Dispatchers.Default, fa, fb, fc) +): Race3 { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + } + return raceN(Dispatchers.Default, fa, fb, fc) +} /** * Races the participants [fa], [fb] & [fc] on the provided [CoroutineContext]. @@ -52,13 +71,19 @@ public suspend inline fun raceN( * * @see raceN for a function that ensures operations run in parallel on the [Dispatchers.Default]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA") public suspend inline fun raceN( ctx: CoroutineContext = EmptyCoroutineContext, crossinline fa: suspend CoroutineScope.() -> A, crossinline fb: suspend CoroutineScope.() -> B, crossinline fc: suspend CoroutineScope.() -> C -): Race3 = - coroutineScope { +): Race3 { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + callsInPlace(fb, InvocationKind.AT_MOST_ONCE) + callsInPlace(fc, InvocationKind.AT_MOST_ONCE) + } + return coroutineScope { val a = async(ctx) { fa() } val b = async(ctx) { fb() } val c = async(ctx) { fc() } @@ -74,6 +99,7 @@ public suspend inline fun raceN( } } } +} @PublishedApi internal suspend fun cancelAndCompose(first: Deferred<*>, second: Deferred<*>): Unit { 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 d14d86dd3b4..286d49ec099 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,3 +1,5 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.fx.coroutines import arrow.AutoCloseScope @@ -11,6 +13,9 @@ import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.withContext +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.coroutines.cancellation.CancellationException @DslMarker @@ -355,13 +360,20 @@ public fun resource(block: suspend ResourceScope.() -> A): Resource = blo @OptIn(DelicateCoroutinesApi::class) @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") public suspend inline fun resourceScope(action: suspend ResourceScope.() -> A): A { + contract { + callsInPlace(action, InvocationKind.EXACTLY_ONCE) + } val (scope, cancelAll) = resource { this }.allocate() return finalizeCase({ scope.action() }) { cancelAll(it) } } @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") -public suspend inline infix fun Resource.use(f: suspend (A) -> B): B = - resourceScope { f(bind()) } +public suspend inline infix fun Resource.use(f: suspend (A) -> B): B { + contract { + callsInPlace(f, InvocationKind.EXACTLY_ONCE) + } + return resourceScope { f(bind()) } +} /** * Construct a [Resource] from an allocating function [acquire] and a release function [release]. @@ -508,7 +520,18 @@ internal expect val IODispatcher: CoroutineDispatcher public suspend fun ResourceScope.autoCloseable( closingDispatcher: CoroutineDispatcher = IODispatcher, autoCloseable: suspend () -> A, -): A = install({ autoCloseable() } ) { s: A, _ -> withContext(closingDispatcher) { s.close() } } +): A { + contract { + callsInPlace(autoCloseable, InvocationKind.EXACTLY_ONCE) + } + // This is install({ autoCloseable() } ) { s: A, _ -> withContext(closingDispatcher) { s.close() } } + // but inlined because `install` can't have a contract (since it's a member) + return withContext(NonCancellable) { + val s = autoCloseable() + onRelease { withContext(closingDispatcher) { s.close() } } + s + } +} public fun autoCloseable( closingDispatcher: CoroutineDispatcher = IODispatcher, diff --git a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/await/AwaitAllScope.kt b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/await/AwaitAllScope.kt index c48654427a7..45d2841948b 100644 --- a/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/await/AwaitAllScope.kt +++ b/arrow-libs/fx/arrow-fx-coroutines/src/commonMain/kotlin/arrow/fx/coroutines/await/AwaitAllScope.kt @@ -1,3 +1,5 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.fx.coroutines.await import arrow.atomic.Atomic @@ -9,6 +11,9 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.InternalForInheritanceCoroutinesApi import kotlinx.coroutines.awaitAll as coroutinesAwaitAll import kotlinx.coroutines.coroutineScope +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -20,12 +25,22 @@ public annotation class ExperimentalAwaitAllApi @ExperimentalAwaitAllApi public suspend fun awaitAll( block: suspend AwaitAllScope.() -> A -): A = coroutineScope { block(AwaitAllScope(this)) } +): A { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + return coroutineScope { block(AwaitAllScope(this)) } +} @ExperimentalAwaitAllApi public suspend fun CoroutineScope.awaitAll( block: suspend AwaitAllScope.() -> A -): A = block(AwaitAllScope(this)) +): A { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + return block(AwaitAllScope(this)) +} /** * Within an [AwaitAllScope], any call to [kotlinx.coroutines.Deferred.await] diff --git a/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/STM.kt b/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/STM.kt index 9f80b08a98e..838b5e08496 100644 --- a/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/STM.kt +++ b/arrow-libs/fx/arrow-fx-stm/src/commonMain/kotlin/arrow/fx/stm/STM.kt @@ -1,8 +1,13 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.fx.stm import arrow.fx.stm.internal.STMTransaction import arrow.fx.stm.internal.alterHamtWithHash import arrow.fx.stm.internal.lookupHamtWithHash +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.reflect.KProperty /** @@ -1692,4 +1697,9 @@ public fun STM.check(b: Boolean): Unit = if (b.not()) retry() else Unit * Rethrows all exceptions not caught by inside [f]. Remember to use [STM.catch] to handle exceptions as `try {} catch` will not handle transaction * state properly! */ -public suspend fun atomically(f: STM.() -> A): A = STMTransaction(f).commit() +public suspend fun atomically(f: STM.() -> A): A { + contract { + callsInPlace(f, InvocationKind.AT_LEAST_ONCE) + } + return STMTransaction().commit(f) +} 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 195c6b9f99e..9b47ea2ac16 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 @@ -148,7 +148,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 = Atomic>>(emptyList()) + private val waiting = Atomic>(emptyList()) override fun hashCode(): Int = id.hashCode() @@ -196,7 +196,7 @@ public class TVar internal constructor(a: A) { * This does not happen implicitly on [release] because release may also write the same value back on * normal lock release. */ - internal fun registerWaiting(trans: STMTransaction<*>, expected: A): Boolean { + internal fun registerWaiting(trans: STMTransaction, expected: A): Boolean { if (value !== expected) { trans.getCont()?.resume(Unit) return false @@ -212,7 +212,7 @@ public class TVar internal constructor(a: A) { /** * A transaction resumed so remove it from the [TVar] */ - internal fun removeWaiting(trans: STMTransaction<*>): Unit { + internal fun removeWaiting(trans: STMTransaction): Unit { waiting.update { it.filter { it !== trans } } } 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 c2d624ac7a3..fe6a2ed81ad 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,4 +1,5 @@ @file:Suppress("UNCHECKED_CAST") +@file:OptIn(ExperimentalContracts::class) package arrow.fx.stm.internal @@ -7,6 +8,9 @@ import arrow.atomic.value import arrow.fx.stm.STM import arrow.fx.stm.TVar import kotlinx.coroutines.suspendCancellableCoroutine +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.coroutines.Continuation /** @@ -168,7 +172,7 @@ public expect object RetryException : Throwable * * Keeps the continuation that [TVar]'s use to resume this transaction. */ -internal class STMTransaction(val f: STM.() -> A) { +internal class STMTransaction { private val cont = Atomic?>(null) /** @@ -183,7 +187,10 @@ internal class STMTransaction(val f: STM.() -> A) { // If they both pass a threshold we should probably kill the transaction and throw // "live-locked" transactions are those that are continuously retry due to accessing variables with high contention and // taking longer than the transactions updating those variables. - suspend fun commit(): A { + suspend fun commit(f: STM.() -> A): A { + contract { + callsInPlace(f, InvocationKind.AT_LEAST_ONCE) + } loop@ while (true) { val frame = STMFrame() try { diff --git a/arrow-libs/optics/arrow-optics-compose/src/commonMain/kotlin/arrow/optics/Copy.kt b/arrow-libs/optics/arrow-optics-compose/src/commonMain/kotlin/arrow/optics/Copy.kt index 38d47efe631..6251c154cbd 100644 --- a/arrow-libs/optics/arrow-optics-compose/src/commonMain/kotlin/arrow/optics/Copy.kt +++ b/arrow-libs/optics/arrow-optics-compose/src/commonMain/kotlin/arrow/optics/Copy.kt @@ -1,4 +1,5 @@ @file:JvmName("ComposeCopyKt") +@file:OptIn(ExperimentalContracts::class) package arrow.optics @@ -6,13 +7,18 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.snapshots.Snapshot import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.update +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.jvm.JvmName /** * Modifies the value in this [MutableState] * by applying the function [block] to the current value. */ +@Suppress("WRONG_INVOCATION_KIND") // withMutableSnapshot doesn't have a contract public inline fun MutableState.update(crossinline block: (T) -> T) { + contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } Snapshot.withMutableSnapshot { value = block(value) } diff --git a/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/CircuitBreaker.kt b/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/CircuitBreaker.kt index 51a89feac7e..345087f2b11 100644 --- a/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/CircuitBreaker.kt +++ b/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/CircuitBreaker.kt @@ -1,8 +1,13 @@ +@file:OptIn(ExperimentalContracts::class) + package arrow.resilience import arrow.atomic.Atomic import arrow.core.Either import arrow.core.identity +import arrow.core.left +import arrow.core.nonFatalOrThrow +import arrow.core.right import arrow.resilience.CircuitBreaker.State.Closed import arrow.resilience.CircuitBreaker.State.HalfOpen import arrow.resilience.CircuitBreaker.State.Open @@ -10,6 +15,9 @@ import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.withContext +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.time.Duration import kotlin.time.Duration.Companion.nanoseconds import kotlin.time.TimeMark @@ -158,20 +166,34 @@ private constructor( * Returns a new task that upon execution will execute the given task, but with the protection of this circuit breaker. * If an exception in [fa] occurs, other than an [ExecutionRejected] exception, it will be rethrown. */ - public suspend fun protectEither(fa: suspend () -> A): Either = - try { + public suspend fun protectEither(fa: suspend () -> A): Either { + contract { + callsInPlace(fa, InvocationKind.AT_MOST_ONCE) + } + return try { Either.Right(protectOrThrow(fa)) } catch (e: ExecutionRejected) { Either.Left(e) } + } /** * Returns a new task that upon execution will execute the given task, but with the protection of this circuit breaker. * If an exception in [fa] occurs it will be rethrown */ - public tailrec suspend fun protectOrThrow(fa: suspend () -> A): A = - when (val curr = state.get()) { - is Closed -> markOrResetFailures(Either.catch { fa() }) + public tailrec suspend fun protectOrThrow(fa: suspend () -> A): A { + contract { + callsInPlace(fa, InvocationKind.EXACTLY_ONCE) + } + return when (val curr = state.get()) { + is Closed -> { + // This is markOrResetFailures(Either.catch { fa() }), but inlined to make the compiler happy with the contract + try { + markOrResetFailures(fa().right()) + } catch (e: Throwable) { + markOrResetFailures(e.nonFatalOrThrow().left()) + } + } is Open -> { if (curr.expiresAt.hasPassedNow()) { // The Open state has expired, so we are transition to HalfOpen and attempt to close the CircuitBreaker @@ -194,6 +216,7 @@ private constructor( throw ExecutionRejected("Rejected because the CircuitBreaker is in the HalfOpen state", curr) } } + } /** Function for counting failures in the `Closed` state, triggering the `Open` state if necessary.*/ private tailrec suspend fun markOrResetFailures(result: Either): A = @@ -245,24 +268,29 @@ private constructor( resetTimeout: Duration, awaitClose: CompletableDeferred, lastStartedAt: TimeMark - ): A = try { - onHalfOpen.invoke() - task.invoke() - } catch (e: CancellationException) { - // 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(state.get().openingStrategy ,lastStartedAt, resetTimeout, awaitClose)) - onOpenAndThrow(e) - } catch (e: Throwable) { - // Failed reset, which means we go back in the Open state with new expiry val nextTimeout - val value: Duration = (resetTimeout * exponentialBackoffFactor) - val nextTimeout = if (maxResetTimeout.isFinite() && value > maxResetTimeout) maxResetTimeout else value - state.set(Open(state.get().openingStrategy, timeSource.markNow(), nextTimeout, awaitClose)) - onOpenAndThrow(e) - }.also { - // While in HalfOpen only a reset attempt is allowed to update the state, so setting this directly is safe - state.set(Closed(state.get().openingStrategy.resetFailuresCount())) - awaitClose.complete(Unit) - onClosed.invoke() + ): A { + contract { + callsInPlace(task, InvocationKind.EXACTLY_ONCE) + } + return try { + onHalfOpen.invoke() + task.invoke() + } catch (e: CancellationException) { + // 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(state.get().openingStrategy, lastStartedAt, resetTimeout, awaitClose)) + onOpenAndThrow(e) + } catch (e: Throwable) { + // Failed reset, which means we go back in the Open state with new expiry val nextTimeout + val value: Duration = (resetTimeout * exponentialBackoffFactor) + val nextTimeout = if (maxResetTimeout.isFinite() && value > maxResetTimeout) maxResetTimeout else value + state.set(Open(state.get().openingStrategy, timeSource.markNow(), nextTimeout, awaitClose)) + onOpenAndThrow(e) + }.also { + // While in HalfOpen only a reset attempt is allowed to update the state, so setting this directly is safe + state.set(Closed(state.get().openingStrategy.resetFailuresCount())) + awaitClose.complete(Unit) + onClosed.invoke() + } } private suspend fun onOpenAndThrow(original: Throwable): Nothing { diff --git a/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/Schedule.kt b/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/Schedule.kt index 7fff24ee457..e6f88f50721 100644 --- a/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/Schedule.kt +++ b/arrow-libs/resilience/arrow-resilience/src/commonMain/kotlin/arrow/resilience/Schedule.kt @@ -1,4 +1,4 @@ -@file:OptIn(ExperimentalTypeInference::class) +@file:OptIn(ExperimentalTypeInference::class, ExperimentalContracts::class) package arrow.resilience @@ -11,7 +11,7 @@ import arrow.core.merge import arrow.core.nonFatalOrThrow import arrow.core.raise.Raise import arrow.core.raise.either -import arrow.core.raise.fold +import arrow.core.raise.recover import arrow.core.right import arrow.core.some import arrow.resilience.Schedule.Companion.identity @@ -21,6 +21,9 @@ import kotlinx.coroutines.currentCoroutineContext import kotlinx.coroutines.delay import kotlinx.coroutines.ensureActive import kotlinx.coroutines.flow.retry +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract import kotlin.experimental.ExperimentalTypeInference import kotlin.math.pow import kotlin.random.Random @@ -414,16 +417,26 @@ public fun interface Schedule { */ public suspend inline fun Schedule.retry( noinline action: suspend () -> A -): A = retry(E::class, action) +): A { + contract { + callsInPlace(action, InvocationKind.AT_LEAST_ONCE) + } + return retry(E::class, action) +} /** * Retries [action] using any [E] that occurred as the input to the [Schedule]. * It will throw the last exception if the [Schedule] is exhausted, and ignores the output of the [Schedule]. */ +@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND") public suspend fun Schedule.retry( exceptionClass: KClass, action: suspend () -> A -): A = retryOrElse(exceptionClass, action) { e, _ -> throw e } +): A { + // For an A to be returned, action must be called. + contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) } + return retryOrElse(exceptionClass, { action() }) { e, _ -> throw e } +} /** * Retries [action] using any [E] that occurred as the input to the [Schedule]. @@ -522,25 +535,22 @@ public suspend inline fun Raise.retry( schedule: Schedule, @BuilderInference action: Raise.() -> Result, ): Result { + contract { + callsInPlace(action, InvocationKind.AT_LEAST_ONCE) + } var step = schedule.step while (true) { currentCoroutineContext().ensureActive() - fold( - action, - recover = { error -> - when (val decision = step(error)) { - is Continue -> { - if (decision.delay != ZERO) delay(decision.delay) - step = decision.step - } - - is Done -> raise(error) + recover({ return action(this) }) { error -> + when (val decision = step(error)) { + is Continue -> { + if (decision.delay != ZERO) delay(decision.delay) + step = decision.step } - }, - transform = { result -> - return result - }, - ) + + is Done -> raise(error) + } + } } }