Skip to content

Commit

Permalink
Reduce suppressions
Browse files Browse the repository at this point in the history
  • Loading branch information
kyay10 committed Nov 15, 2024
1 parent 6b5f5c9 commit d105648
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,13 @@
package arrow.core

import arrow.core.raise.RaiseAccumulate
import arrow.core.raise.either
import arrow.core.raise.withError
import arrow.core.raise.mapOrAccumulate as raiseMapOrAccumulate
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import kotlin.collections.unzip as stdlibUnzip
import kotlin.experimental.ExperimentalTypeInference
import kotlin.jvm.JvmInline
import kotlin.jvm.JvmName
import kotlin.collections.unzip as stdlibUnzip

public typealias Nel<A> = NonEmptyList<A>

Expand Down Expand Up @@ -215,21 +212,21 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
public override operator fun plus(element: @UnsafeVariance A): NonEmptyList<A> =
NonEmptyList(all + element)

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B> foldLeft(b: B, f: (B, A) -> B): B {
contract { callsInPlace(f, InvocationKind.AT_LEAST_ONCE) }
return all.fold(b, f)
var accumulator = f(b, head)
for (element in tail) accumulator = f(accumulator, element)
return accumulator
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B> coflatMap(f: (NonEmptyList<A>) -> B): NonEmptyList<B> {
contract { callsInPlace(f, InvocationKind.AT_LEAST_ONCE) }
var current = this
return buildList {
var current = all
while (current.isNotEmpty()) {
add(f(NonEmptyList(current)))
current = current.drop(1)
}
do {
add(f(current))
current = current.drop(1).toNonEmptyListOrNull() ?: break
} while (true)
}.let(::NonEmptyList)
}

Expand All @@ -245,10 +242,9 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
public fun <B> padZip(other: NonEmptyList<B>): NonEmptyList<Pair<A?, B?>> =
padZip(other, { it to null }, { null to it }, { a, b -> a to b })

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C> padZip(other: NonEmptyList<B>, left: (A) -> C, right: (B) -> C, both: (A, B) -> C): NonEmptyList<C> {
contract { callsInPlace(both, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.padZip(other, left, right, both))
return NonEmptyList(both(head, other.head), tail.padZip(other.tail, left, right) { a, b -> both(a, b) })
}

public companion object {
Expand All @@ -260,37 +256,33 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
public fun <B> zip(fb: NonEmptyList<B>): NonEmptyList<Pair<A, B>> =
zip(fb, ::Pair)

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, Z> zip(
b: NonEmptyList<B>,
map: (A, B) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, map))
return NonEmptyList(map(head, b.head), tail.zip(b.tail) { a, bb -> map(a, bb) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
map: (A, B, C) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, map))
return NonEmptyList(map(head, b.head, c.head), tail.zip(b.tail, c.tail) { a, bb, cc -> map(a, bb, cc) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, D, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
d: NonEmptyList<D>,
map: (A, B, C, D) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, d.all, map))
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) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, D, E, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
Expand All @@ -299,10 +291,9 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
map: (A, B, C, D, E) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, d.all, e.all, map))
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) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, D, E, F, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
Expand All @@ -312,10 +303,9 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
map: (A, B, C, D, E, F) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, map))
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) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, D, E, F, G, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
Expand All @@ -326,10 +316,9 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
map: (A, B, C, D, E, F, G) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, map))
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) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, D, E, F, G, H, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
Expand All @@ -341,10 +330,9 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
map: (A, B, C, D, E, F, G, H) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, map))
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) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, D, E, F, G, H, I, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
Expand All @@ -357,10 +345,9 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
map: (A, B, C, D, E, F, G, H, I) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, i.all, map))
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) })
}

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public inline fun <B, C, D, E, F, G, H, I, J, Z> zip(
b: NonEmptyList<B>,
c: NonEmptyList<C>,
Expand All @@ -374,7 +361,7 @@ public value class NonEmptyList<out A> @PublishedApi internal constructor(
map: (A, B, C, D, E, F, G, H, I, J) -> Z
): NonEmptyList<Z> {
contract { callsInPlace(map, InvocationKind.AT_LEAST_ONCE) }
return NonEmptyList(all.zip(b.all, c.all, d.all, e.all, f.all, g.all, h.all, i.all, j.all, map))
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) })
}
}

Expand Down Expand Up @@ -410,10 +397,10 @@ public inline fun <T : Comparable<T>> NonEmptyList<T>.max(): T =
public fun <A, B> NonEmptyList<Pair<A, B>>.unzip(): Pair<NonEmptyList<A>, NonEmptyList<B>> =
this.unzip(::identity)

@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
@Suppress("WRONG_INVOCATION_KIND")
public inline fun <A, B, C> NonEmptyList<C>.unzip(f: (C) -> Pair<A, B>): Pair<NonEmptyList<A>, NonEmptyList<B>> {
contract { callsInPlace(f, InvocationKind.AT_LEAST_ONCE) }
return map(f).stdlibUnzip().let { (l1, l2) ->
return map { f(it) }.stdlibUnzip().let { (l1, l2) ->
l1.toNonEmptyListOrNull()!! to l2.toNonEmptyListOrNull()!!
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,7 @@ public inline fun <Error, OtherError, A> Raise<Error>.withError(
callsInPlace(transform, AT_MOST_ONCE)
// This is correct, despite compiler complaining, because we don't actually "handle" any errors from `block`,
// we just transform them and re-raise them.
// This can be proven by inlining `recover` and subsequently `fold` and observing that the compiler is happy
callsInPlace(block, EXACTLY_ONCE)
}
return recover(block) { raise(transform(it)) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ public inline fun <Error, A, B, C, D, E, F, G, H, I> Raise<NonEmptyList<Error>>.
* and how to use it in [validation](https://arrow-kt.io/learn/typed-errors/validation/).
*/
@RaiseDSL @OptIn(ExperimentalRaiseAccumulateApi::class)
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
@Suppress("WRONG_INVOCATION_KIND")
public inline fun <Error, A, B, C, D, E, F, G, H, I, J> Raise<NonEmptyList<Error>>.zipOrAccumulate(
@BuilderInference action1: RaiseAccumulate<Error>.() -> A,
@BuilderInference action2: RaiseAccumulate<Error>.() -> B,
Expand All @@ -597,15 +597,15 @@ public inline fun <Error, A, B, C, D, E, F, G, H, I, J> Raise<NonEmptyList<Error
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)
}
}
Expand Down Expand Up @@ -750,13 +750,15 @@ public inline fun <Error, A, B> Raise<NonEmptyList<Error>>.mapOrAccumulate(
* and how to use it in [validation](https://arrow-kt.io/learn/typed-errors/validation/).
*/
@RaiseDSL
@Suppress("WRONG_INVOCATION_KIND", "LEAKED_IN_PLACE_LAMBDA")
@Suppress("WRONG_INVOCATION_KIND")
public inline fun <Error, A, B> Raise<NonEmptyList<Error>>.mapOrAccumulate(
nonEmptyList: NonEmptyList<A>,
@BuilderInference transform: RaiseAccumulate<Error>.(A) -> B
): NonEmptyList<B> {
// 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).toNonEmptyListOrNull())
return requireNotNull(mapOrAccumulate(nonEmptyList.all) { transform(it) }.toNonEmptyListOrNull())
}

/**
Expand All @@ -767,7 +769,7 @@ public inline fun <Error, A, B> Raise<NonEmptyList<Error>>.mapOrAccumulate(
* and how to use it in [validation](https://arrow-kt.io/learn/typed-errors/validation/).
*/
@RaiseDSL
@Suppress("WRONG_INVOCATION_KIND", "LEAKED_IN_PLACE_LAMBDA")
@Suppress("WRONG_INVOCATION_KIND")
public inline fun <Error, A, B> Raise<NonEmptyList<Error>>.mapOrAccumulate(
nonEmptySet: NonEmptySet<A>,
@BuilderInference transform: RaiseAccumulate<Error>.(A) -> B
Expand Down Expand Up @@ -839,15 +841,12 @@ public inline fun <Error, A> Raise<NonEmptyList<Error>>.accumulate(
}

@ExperimentalRaiseAccumulateApi
@Suppress("LEAKED_IN_PLACE_LAMBDA")
public inline fun <Error, A, R> accumulate(
raise: (Raise<NonEmptyList<Error>>.() -> A) -> R,
crossinline block: RaiseAccumulate<Error>.() -> A
): R {
contract {
callsInPlace(raise, EXACTLY_ONCE)
// Technically wrong, but left here out of convenience since most values for `raise` only uses `block` once
callsInPlace(block, AT_MOST_ONCE)
}
return raise { accumulate(block) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ public suspend inline fun <E, A, B, C, D> Raise<E>.parZipOrAccumulate(
crossinline transform: suspend CoroutineScope.(A, B, C) -> D
): 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.EXACTLY_ONCE)
callsInPlace(fb, InvocationKind.EXACTLY_ONCE)
callsInPlace(fc, InvocationKind.EXACTLY_ONCE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,16 +517,18 @@ internal expect val IODispatcher: CoroutineDispatcher
* <!--- KNIT example-resource-10.kt -->
*/
@ResourceDSL
@Suppress("LEAKED_IN_PLACE_LAMBDA", "WRONG_INVOCATION_KIND")
public suspend fun <A : AutoCloseable> ResourceScope.autoCloseable(
closingDispatcher: CoroutineDispatcher = IODispatcher,
autoCloseable: suspend () -> A,
): A {
contract {
// Complaining because `install` has no contract because it's a member of an interface
callsInPlace(autoCloseable, InvocationKind.EXACTLY_ONCE)
}
return install({ autoCloseable() }) { s: A, _ -> withContext(closingDispatcher) { s.close() } }
return withContext(NonCancellable) {
val s = autoCloseable()
onRelease { withContext(closingDispatcher) { s.close() } }
s
}
}

public fun <A : AutoCloseable> autoCloseable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,15 +428,14 @@ public suspend inline fun <reified E: Throwable, A> Schedule<E, *>.retry(
* 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")
@Suppress("WRONG_INVOCATION_KIND")
public suspend inline fun <E: Throwable, A> Schedule<E, *>.retry(
exceptionClass: KClass<E>,
action: suspend () -> A
): A {
contract {
callsInPlace(action, InvocationKind.AT_LEAST_ONCE) // because if the schedule is exhausted, it will throw
}
return retryOrElse(exceptionClass, action) { e, _ -> throw e }
// For an A to be returned, action must be called.
contract { callsInPlace(action, InvocationKind.AT_LEAST_ONCE) }
return retryOrElse(exceptionClass, { action() }) { e, _ -> throw e }
}

/**
Expand Down

0 comments on commit d105648

Please sign in to comment.