Skip to content

Commit

Permalink
[Arrow 2.0] Effect without suspending shift (#2797)
Browse files Browse the repository at this point in the history
* Shift without suspend, inline all the rest
* Add new error handlers signatures
* Make ShiftCancellationException private
  • Loading branch information
nomisRev authored Sep 2, 2022
1 parent cfd44d9 commit bf07694
Show file tree
Hide file tree
Showing 73 changed files with 1,477 additions and 2,702 deletions.
686 changes: 240 additions & 446 deletions arrow-libs/core/arrow-core/api/arrow-core.api

Large diffs are not rendered by default.

170 changes: 58 additions & 112 deletions arrow-libs/core/arrow-core/src/commonMain/kotlin/arrow/core/Either.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package arrow.core
import arrow.core.Either.Companion.resolve
import arrow.core.Either.Left
import arrow.core.Either.Right
import arrow.core.continuations.Shift
import arrow.core.continuations.either
import arrow.core.continuations.ensure
import arrow.typeclasses.Monoid
import arrow.typeclasses.Semigroup
import kotlin.experimental.ExperimentalTypeInference
Expand Down Expand Up @@ -1229,6 +1232,32 @@ public sealed class Either<out A, out B> {
map { Unit }
}

/**
* Recover from [E] when encountering [Left].
* You can either return a new value of [A], or short-circuit by shifting with a value of [E2].
*
* ```kotlin
* import arrow.core.Either
* import arrow.core.left
* import arrow.core.recover
*
* object User
* object Error
*
* val error: Either<Error, User> = Error.left()
*
* val a: Either<Error, User> = error.recover { error -> User } // Either.Right(User)
* val b: Either<String, User> = error.recover { error -> shift("other-failure") } // Either.Left(other-failure)
* val c: Either<Nothing, User> = error.recover { error -> User } // Either.Right(User)
* ```
* <!--- KNIT example-either-45.kt -->
*/
public inline fun <E2, E, A> Either<E, A>.recover(recover: Shift<E2>.(E) -> A): Either<E2, A> =
when (this) {
is Right -> this
is Left -> either { recover(value) }
}

/**
* Binds the given function across [Right].
*
Expand Down Expand Up @@ -1257,7 +1286,7 @@ public fun <A, B> Either<A, Either<A, B>>.flatten(): Either<A, B> =
* Left(12).getOrElse { 17 } // Result: 17
* }
* ```
* <!--- KNIT example-either-45.kt -->
* <!--- KNIT example-either-46.kt -->
*/
public inline fun <B> Either<*, B>.getOrElse(default: () -> B): B =
fold({ default() }, ::identity)
Expand All @@ -1275,7 +1304,7 @@ public inline fun <B> Either<*, B>.getOrElse(default: () -> B): B =
* Left(12).orNull() // Result: null
* }
* ```
* <!--- KNIT example-either-46.kt -->
* <!--- KNIT example-either-47.kt -->
*/
@Deprecated("Duplicated API. Please use Either's member function orNull. This will be removed towards Arrow 2.0", ReplaceWith("orNull()"))
public fun <B> Either<*, B>.orNull(): B? =
Expand All @@ -1296,7 +1325,7 @@ public fun <B> Either<*, B>.orNull(): B? =
* Left(12).getOrHandle { it + 5 } // Result: 17
* }
* ```
* <!--- KNIT example-either-47.kt -->
* <!--- KNIT example-either-48.kt -->
*/
public inline fun <A, B> Either<A, B>.getOrHandle(default: (A) -> B): B =
fold({ default(it) }, ::identity)
Expand Down Expand Up @@ -1324,7 +1353,7 @@ public inline fun <A, B> Either<A, B>.getOrHandle(default: (A) -> B): B =
* left.filterOrElse({ it > 10 }, { -1 }) // Result: Left(12)
* }
* ```
* <!--- KNIT example-either-48.kt -->
* <!--- KNIT example-either-49.kt -->
*/
public inline fun <A, B> Either<A, B>.filterOrElse(predicate: (B) -> Boolean, default: () -> A): Either<A, B> =
flatMap { if (predicate(it)) Right(it) else Left(default()) }
Expand Down Expand Up @@ -1357,7 +1386,7 @@ public inline fun <A, B> Either<A, B>.filterOrElse(predicate: (B) -> Boolean, de
* //sampleEnd
* }
* ```
* <!--- KNIT example-either-49.kt -->
* <!--- KNIT example-either-50.kt -->
*/
public inline fun <A, B> Either<A, B>.filterOrOther(predicate: (B) -> Boolean, default: (B) -> A): Either<A, B> =
flatMap {
Expand All @@ -1379,7 +1408,7 @@ public inline fun <A, B> Either<A, B>.filterOrOther(predicate: (B) -> Boolean, d
* Left(12).merge() // Result: 12
* }
* ```
* <!--- KNIT example-either-50.kt -->
* <!--- KNIT example-either-51.kt -->
*/
public inline fun <A> Either<A, A>.merge(): A =
fold(::identity, ::identity)
Expand All @@ -1404,7 +1433,7 @@ public inline fun <A> Either<A, A>.merge(): A =
* Left(12).leftIfNull({ -1 }) // Result: Left(12)
* }
* ```
* <!--- KNIT example-either-51.kt -->
* <!--- KNIT example-either-52.kt -->
*/
public inline fun <A, B> Either<A, B?>.leftIfNull(default: () -> A): Either<A, B> =
flatMap { it.rightIfNotNull { default() } }
Expand Down Expand Up @@ -1456,7 +1485,7 @@ public fun <A> A.right(): Either<Nothing, A> = Right(this)
* null.rightIfNotNull { "left" } // Left(a="left")
* }
* ```
* <!--- KNIT example-either-52.kt -->
* <!--- KNIT example-either-53.kt -->
*/
public inline fun <A, B> B?.rightIfNotNull(default: () -> A): Either<A, B> = when (this) {
null -> Left(default())
Expand All @@ -1477,22 +1506,13 @@ public inline fun <A> Any?.rightIfNull(default: () -> A): Either<A, Nothing?> =
* This is like `flatMap` for the exception.
*/
public inline fun <A, B, C> Either<A, B>.handleErrorWith(f: (A) -> Either<C, B>): Either<C, B> =
when (this) {
is Left -> f(this.value)
is Right -> this
}
recover { f(it).bind() }

public inline fun <A, B> Either<A, B>.handleError(f: (A) -> B): Either<A, B> =
when (this) {
is Left -> f(value).right()
is Right -> this
}
recover { f(it) }

public inline fun <A, B, C> Either<A, B>.redeem(fe: (A) -> C, fa: (B) -> C): Either<A, C> =
when (this) {
is Left -> fe(value).right()
is Right -> map(fa)
}
either { fa(bind()) }.recover { fe(it) }

public operator fun <A : Comparable<A>, B : Comparable<B>> Either<A, B>.compareTo(other: Either<A, B>): Int =
fold(
Expand Down Expand Up @@ -1531,7 +1551,7 @@ public fun <A, B> Iterable<Either<A, B>>.combineAll(MA: Monoid<A>, MB: Monoid<B>
* println(chars)
* }
* ```
* <!--- KNIT example-either-53.kt -->
* <!--- KNIT example-either-54.kt -->
*/
public fun <A, C, B : C> Either<A, B>.widen(): Either<A, C> =
this
Expand All @@ -1540,49 +1560,25 @@ public fun <AA, A : AA, B> Either<A, B>.leftWiden(): Either<AA, B> =
this

public fun <A, B, C, D> Either<A, B>.zip(fb: Either<A, C>, f: (B, C) -> D): Either<A, D> =
flatMap { b ->
fb.map { c -> f(b, c) }
}
either { f(bind(), fb.bind()) }

public fun <A, B, C> Either<A, B>.zip(fb: Either<A, C>): Either<A, Pair<B, C>> =
flatMap { a ->
fb.map { b -> Pair(a, b) }
}
either { Pair(bind(), fb.bind()) }

public inline fun <A, B, C, D, E> Either<A, B>.zip(
c: Either<A, C>,
d: Either<A, D>,
map: (B, C, D) -> E
): Either<A, E> =
zip(
c,
d,
Right.unit,
Right.unit,
Right.unit,
Right.unit,
Right.unit,
Right.unit,
Right.unit
) { b, c, d, _, _, _, _, _, _, _ -> map(b, c, d) }
either { map(bind(), c.bind(), d.bind()) }

public inline fun <A, B, C, D, E, F> Either<A, B>.zip(
c: Either<A, C>,
d: Either<A, D>,
e: Either<A, E>,
map: (B, C, D, E) -> F
): Either<A, F> =
zip(
c,
d,
e,
Right.unit,
Right.unit,
Right.unit,
Right.unit,
Right.unit,
Right.unit
) { b, c, d, e, _, _, _, _, _, _ -> map(b, c, d, e) }
either { map(bind(), c.bind(), d.bind(), e.bind()) }

public inline fun <A, B, C, D, E, F, G> Either<A, B>.zip(
c: Either<A, C>,
Expand All @@ -1591,17 +1587,7 @@ public inline fun <A, B, C, D, E, F, G> Either<A, B>.zip(
f: Either<A, F>,
map: (B, C, D, E, F) -> G
): Either<A, G> =
zip(
c,
d,
e,
f,
Right.unit,
Right.unit,
Right.unit,
Right.unit,
Right.unit
) { b, c, d, e, f, _, _, _, _, _ -> map(b, c, d, e, f) }
either { map(bind(), c.bind(), d.bind(), e.bind(), f.bind()) }

public inline fun <A, B, C, D, E, F, G, H> Either<A, B>.zip(
c: Either<A, C>,
Expand All @@ -1611,16 +1597,7 @@ public inline fun <A, B, C, D, E, F, G, H> Either<A, B>.zip(
g: Either<A, G>,
map: (B, C, D, E, F, G) -> H
): Either<A, H> =
zip(c, d, e, f, g, Right.unit, Right.unit, Right.unit, Right.unit) { b, c, d, e, f, g, _, _, _, _ ->
map(
b,
c,
d,
e,
f,
g
)
}
either { map(bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind()) }

public inline fun <A, B, C, D, E, F, G, H, I> Either<A, B>.zip(
c: Either<A, C>,
Expand All @@ -1631,17 +1608,7 @@ public inline fun <A, B, C, D, E, F, G, H, I> Either<A, B>.zip(
h: Either<A, H>,
map: (B, C, D, E, F, G, H) -> I
): Either<A, I> =
zip(c, d, e, f, g, h, Right.unit, Right.unit, Right.unit) { b, c, d, e, f, g, h, _, _, _ ->
map(
b,
c,
d,
e,
f,
g,
h
)
}
either { map(bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind()) }

public inline fun <A, B, C, D, E, F, G, H, I, J> Either<A, B>.zip(
c: Either<A, C>,
Expand All @@ -1653,7 +1620,7 @@ public inline fun <A, B, C, D, E, F, G, H, I, J> Either<A, B>.zip(
i: Either<A, I>,
map: (B, C, D, E, F, G, H, I) -> J
): Either<A, J> =
zip(c, d, e, f, g, h, i, Right.unit, Right.unit) { b, c, d, e, f, g, h, i, _, _ -> map(b, c, d, e, f, g, h, i) }
either { map(bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind(), i.bind()) }

public inline fun <A, B, C, D, E, F, G, H, I, J, K> Either<A, B>.zip(
c: Either<A, C>,
Expand All @@ -1666,7 +1633,7 @@ public inline fun <A, B, C, D, E, F, G, H, I, J, K> Either<A, B>.zip(
j: Either<A, J>,
map: (B, C, D, E, F, G, H, I, J) -> K
): Either<A, K> =
zip(c, d, e, f, g, h, i, j, Right.unit) { b, c, d, e, f, g, h, i, j, _ -> map(b, c, d, e, f, g, h, i, j) }
either { map(bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind(), i.bind(), j.bind()) }

public inline fun <A, B, C, D, E, F, G, H, I, J, K, L> Either<A, B>.zip(
c: Either<A, C>,
Expand All @@ -1679,28 +1646,9 @@ public inline fun <A, B, C, D, E, F, G, H, I, J, K, L> Either<A, B>.zip(
j: Either<A, J>,
k: Either<A, K>,
map: (B, C, D, E, F, G, H, I, J, K) -> L
): Either<A, L> =
flatMap { bb ->
c.flatMap { cc ->
d.flatMap { dd ->
e.flatMap { ee ->
f.flatMap { ff ->
g.flatMap { gg ->
h.flatMap { hh ->
i.flatMap { ii ->
j.flatMap { jj ->
k.map { kk ->
map(bb, cc, dd, ee, ff, gg, hh, ii, jj, kk)
}
}
}
}
}
}
}
}
}
}
): Either<A, L> = either {
map(bind(), c.bind(), d.bind(), e.bind(), f.bind(), g.bind(), h.bind(), i.bind(), j.bind(), k.bind())
}

public fun <A, B> Either<A, B>.replicate(n: Int, MB: Monoid<B>): Either<A, B> =
if (n <= 0) MB.empty().right()
Expand All @@ -1712,16 +1660,14 @@ public fun <A, B> Either<A, B>.replicate(n: Int, MB: Monoid<B>): Either<A, B> =
}

public inline fun <A, B> Either<A, B>.ensure(error: () -> A, predicate: (B) -> Boolean): Either<A, B> =
when (this) {
is Right -> if (predicate(this.value)) this else error().left()
is Left -> this
either {
val b = bind()
ensure(predicate(b), error)
b
}

public inline fun <A, B, C, D> Either<A, B>.redeemWith(fa: (A) -> Either<C, D>, fb: (B) -> Either<C, D>): Either<C, D> =
when (this) {
is Left -> fa(this.value)
is Right -> fb(this.value)
}
either { fold({ fa(it).bind() }, { fb(it).bind() }) }

public fun <A, B> Either<A, Iterable<B>>.sequence(): List<Either<A, B>> =
traverse(::identity)
Expand Down
Loading

0 comments on commit bf07694

Please sign in to comment.