Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Removes autobind DSL in favor of getOrElse #2968

Merged
merged 4 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 2 additions & 57 deletions arrow-libs/core/arrow-core/api/arrow-core.api

Large diffs are not rendered by default.

116 changes: 57 additions & 59 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 @@ -6,14 +6,11 @@ import arrow.core.Either.Companion.resolve
import arrow.core.Either.Left
import arrow.core.Either.Right
import arrow.core.Either.Right.Companion.unit
import arrow.core.continuations.EagerEffect
import arrow.core.continuations.Effect
import arrow.core.raise.Raise
import arrow.core.raise.either
import arrow.typeclasses.Monoid
import arrow.typeclasses.MonoidDeprecation
import arrow.typeclasses.Semigroup
import arrow.typeclasses.SemigroupDeprecation
import arrow.typeclasses.combine
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
Expand Down Expand Up @@ -1123,7 +1120,7 @@ public sealed class Either<out A, out B> {
}

/**
* Returns the encapsulated value [B] if this instance represents [Either.Right] or `null` if it is [Either.Left].
* Returns the unwrapped value [B] of [Either.Right] or `null` if it is [Either.Left].
*
* ```kotlin
* import arrow.core.Either
Expand All @@ -1145,6 +1142,29 @@ public sealed class Either<out A, out B> {
return getOrElse { null }
}

/**
* Returns the unwrapped value [A] of [Either.Left] or `null` if it is [Either.Right].
*
* ```kotlin
* import arrow.core.Either
* import io.kotest.matchers.shouldBe
*
* fun test() {
* Either.Right(12).leftOrNull() shouldBe null
* Either.Left(12).leftOrNull() shouldBe 12
* }
* ```
* <!--- KNIT example-either-44.kt -->
* <!--- TEST lines.isEmpty() -->
*/
public fun leftOrNull(): A? {
contract {
returnsNotNull() implies (this@Either is Left<A>)
returns(null) implies (this@Either is Right<B>)
}
return fold(::identity) { null }
}

@Deprecated(
"orNone is being renamed to getOrNone to be more consistent with the Kotlin Standard Library naming",
ReplaceWith("getOrNone()")
Expand All @@ -1167,7 +1187,7 @@ public sealed class Either<out A, out B> {
* Either.Left(12).getOrNone() shouldBe None
* }
* ```
* <!--- KNIT example-either-44.kt -->
* <!--- KNIT example-either-45.kt -->
* <!--- TEST lines.isEmpty() -->
*/
public fun getOrNone(): Option<B> = fold({ None }, { Some(it) })
Expand Down Expand Up @@ -1272,7 +1292,7 @@ public sealed class Either<out A, out B> {
* Either.Right("foo").isEmpty() // Result: false
* }
* ```
* <!--- KNIT example-either-45.kt -->
* <!--- KNIT example-either-46.kt -->
*/
@Deprecated(
RedundantAPI + "Use isLeft()",
Expand All @@ -1294,7 +1314,7 @@ public sealed class Either<out A, out B> {
* //sampleEnd
* }
* ```
* <!--- KNIT example-either-46.kt -->
* <!--- KNIT example-either-47.kt -->
*/
@Deprecated(
RedundantAPI + "Use isRight()",
Expand Down Expand Up @@ -1380,25 +1400,21 @@ public sealed class Either<out A, out B> {

@JvmStatic
@JvmName("tryCatch")
public inline fun <R> catch(f: () -> R): Either<Throwable, R> {
contract { callsInPlace(f, InvocationKind.EXACTLY_ONCE) }
return try {
f().right()
} catch (t: Throwable) {
t.nonFatalOrThrow().left()
}
}
public inline fun <R> catch(f: () -> R): Either<Throwable, R> =
arrow.core.raise.catch({ f().right() }) { it.left() }

@JvmStatic
public inline fun <reified T : Throwable, R> catchOrThrow(f: () -> R): Either<T, R> =
arrow.core.raise.catch<T, Either<T, R>>({ f().right() }) { it.left() }

@Deprecated(
RedundantAPI + "Compose catch with flatten instead",
ReplaceWith("catch(f).flatten()")
)
@JvmStatic
@JvmName("tryCatchAndFlatten")
public inline fun <R> catchAndFlatten(f: () -> Either<Throwable, R>): Either<Throwable, R> {
contract { callsInPlace(f, InvocationKind.EXACTLY_ONCE) }
return catch(f).flatten()
}
public inline fun <R> catchAndFlatten(f: () -> Either<Throwable, R>): Either<Throwable, R> =
catch(f).flatten()

@Deprecated(
RedundantAPI + "Compose catch with mapLeft instead",
Expand All @@ -1407,10 +1423,7 @@ public sealed class Either<out A, out B> {
@JvmStatic
@JvmName("tryCatch")
public inline fun <L, R> catch(fe: (Throwable) -> L, f: () -> R): Either<L, R> {
contract {
callsInPlace(f, InvocationKind.EXACTLY_ONCE)
callsInPlace(fe, InvocationKind.AT_MOST_ONCE)
}
contract { callsInPlace(fe, InvocationKind.AT_MOST_ONCE) }
return catch(f).mapLeft(fe)
}

Expand All @@ -1435,7 +1448,6 @@ public sealed class Either<out A, out B> {
unrecoverableState: (throwable: Throwable) -> Either<Throwable, Unit>,
): B {
contract {
callsInPlace(f, InvocationKind.EXACTLY_ONCE)
callsInPlace(success, InvocationKind.AT_MOST_ONCE)
callsInPlace(error, InvocationKind.AT_MOST_ONCE)
callsInPlace(throwable, InvocationKind.AT_MOST_ONCE)
Expand Down Expand Up @@ -1465,7 +1477,7 @@ public sealed class Either<out A, out B> {
* println(result)
* }
* ```
* <!--- KNIT example-either-47.kt -->
* <!--- KNIT example-either-48.kt -->
*/
@JvmStatic
@Deprecated(
Expand Down Expand Up @@ -2003,13 +2015,13 @@ public inline fun <B> Either<*, B>.getOrElse(default: () -> B): B =
* import io.kotest.matchers.shouldBe
*
* fun test() {
* Either.Left(12).getOrElse { it + 5 } shouldBe 17
* Either.Left(12) getOrElse { it + 5 } shouldBe 17
* }
* ```
* <!--- KNIT example-either-48.kt -->
* <!--- KNIT example-either-49.kt -->
* <!--- TEST lines.isEmpty() -->
*/
public inline fun <A, B> Either<A, B>.getOrElse(default: (A) -> B): B {
public inline infix fun <A, B> Either<A, B>.getOrElse(default: (A) -> B): B {
contract { callsInPlace(default, InvocationKind.AT_MOST_ONCE) }
return fold(default, ::identity)
}
Expand All @@ -2027,7 +2039,7 @@ public inline fun <A, B> Either<A, B>.getOrElse(default: (A) -> B): B {
* Left(12).orNull() // Result: null
* }
* ```
* <!--- KNIT example-either-49.kt -->
* <!--- KNIT example-either-50.kt -->
*/
@Deprecated(
"Duplicated API. Please use Either's member function orNull. This will be removed towards Arrow 2.0",
Expand All @@ -2050,7 +2062,7 @@ public fun <B> Either<*, B>.orNull(): B? =
* Left(12).getOrHandle { it + 5 } // Result: 17
* }
* ```
* <!--- KNIT example-either-50.kt -->
* <!--- KNIT example-either-51.kt -->
*/
@Deprecated(
RedundantAPI + "Use other getOrElse signature",
Expand Down Expand Up @@ -2082,7 +2094,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-51.kt -->
* <!--- KNIT example-either-52.kt -->
*/
@Deprecated(
RedundantAPI + "Prefer if-else statement inside either DSL, or replace with explicit flatMap",
Expand Down Expand Up @@ -2119,7 +2131,7 @@ public inline fun <A, B> Either<A, B>.filterOrElse(predicate: (B) -> Boolean, de
* //sampleEnd
* }
* ```
* <!--- KNIT example-either-52.kt -->
* <!--- KNIT example-either-53.kt -->
*/
@Deprecated(
RedundantAPI + "Prefer if-else statement inside either DSL, or replace with explicit flatMap",
Expand All @@ -2142,7 +2154,7 @@ public inline fun <A, B> Either<A, B>.filterOrOther(predicate: (B) -> Boolean, d
* Left(12).merge() // Result: 12
* }
* ```
* <!--- KNIT example-either-53.kt -->
* <!--- KNIT example-either-54.kt -->
* <!--- TEST lines.isEmpty() -->
*/
public inline fun <A> Either<A, A>.merge(): A =
Expand All @@ -2168,7 +2180,7 @@ public inline fun <A> Either<A, A>.merge(): A =
* Left(12).leftIfNull({ -1 }) // Result: Left(12)
* }
* ```
* <!--- KNIT example-either-54.kt -->
* <!--- KNIT example-either-55.kt -->
*/
@Deprecated(
RedundantAPI + "Prefer Kotlin nullable syntax inside either DSL, or replace with explicit flatMap",
Expand Down Expand Up @@ -2229,7 +2241,7 @@ public fun <A> A.right(): Either<Nothing, A> = Right(this)
* null.rightIfNotNull { "left" } // Left(a="left")
* }
* ```
* <!--- KNIT example-either-55.kt -->
* <!--- KNIT example-either-56.kt -->
*/
@Deprecated(
RedundantAPI + "Prefer Kotlin nullable syntax",
Expand Down Expand Up @@ -2345,7 +2357,7 @@ public fun <A, B> Iterable<Either<A, B>>.combineAll(MA: Monoid<A>, MB: Monoid<B>
* println(chars)
* }
* ```
* <!--- KNIT example-either-56.kt -->
* <!--- KNIT example-either-57.kt -->
*/
public fun <A, C, B : C> Either<A, B>.widen(): Either<A, C> =
this
Expand Down Expand Up @@ -2665,7 +2677,7 @@ public fun <E> E.leftNel(): EitherNel<E, Nothing> =
* fallback shouldBe Either.Right(5)
* }
* ```
* <!--- KNIT example-either-57.kt -->
* <!--- KNIT example-either-58.kt -->
* <!--- TEST lines.isEmpty() -->
*
* When shifting a new error [EE] into the [Either.Left] channel,
Expand All @@ -2682,7 +2694,7 @@ public fun <E> E.leftNel(): EitherNel<E, Nothing> =
* listOfErrors shouldBe Either.Left(listOf('e', 'r', 'r', 'o', 'r'))
* }
* ```
* <!--- KNIT example-either-58.kt -->
* <!--- KNIT example-either-59.kt -->
* <!--- TEST lines.isEmpty() -->
*/
@OptIn(ExperimentalTypeInference::class)
Expand All @@ -2695,13 +2707,9 @@ public inline fun <E, EE, A> Either<E, A>.recover(@BuilderInference recover: Rai
}

/**
* Catch allows for transforming [Throwable] in the [Either.Left] side.
* This API is the same as [recover],
* but offers the same APIs for working over [Throwable] as [Effect] & [EagerEffect].
*
* This is useful when working with [Either.catch] since this API offers a `reified` variant.
* The reified version allows you to refine `Throwable` to `T : Throwable`,
* where any `Throwable` not matching the `t is T` predicate will be rethrown.
* Variant of [Either.catchOrThrow] constructor that allows for working with `Either<Throwable, A>`
* by transforming or recovering from [Throwable] as [T] in the [Either.Left] side. This API is the same as [recover].
* This is useful when working with results of [Either.catch] since this API offers a `reified` variant.
*
* ```kotlin
* import arrow.core.Either
Expand All @@ -2712,8 +2720,8 @@ public inline fun <E, EE, A> Either<E, A>.recover(@BuilderInference recover: Rai
* fun test() {
* val left: Either<Throwable, Int> = Either.catch { throw RuntimeException("Boom!") }
*
* val caught: Either<Nothing, Int> = left.catch { _: Throwable -> 1 }
* val failure: Either<String, Int> = left.catch { _: Throwable -> shift("failure") }
* val caught: Either<Nothing, Int> = left.catch { _: RuntimeException -> 1 }
* val failure: Either<String, Int> = left.catch { _: RuntimeException -> shift("failure") }
*
* shouldThrowUnit<RuntimeException> {
* val caught2: Either<Nothing, Int> = left.catch { _: IllegalStateException -> 1 }
Expand All @@ -2723,21 +2731,11 @@ public inline fun <E, EE, A> Either<E, A>.recover(@BuilderInference recover: Rai
* failure shouldBe Either.Left("failure")
* }
* ```
* <!--- KNIT example-either-59.kt -->
* <!--- KNIT example-either-60.kt -->
* <!--- TEST lines.isEmpty() -->
*/
@OptIn(ExperimentalTypeInference::class)
public inline fun <E, A> Either<Throwable, A>.catch(@BuilderInference catch: Raise<E>.(Throwable) -> A): Either<E, A> {
contract { callsInPlace(catch, InvocationKind.AT_MOST_ONCE) }
return when (this) {
is Left -> either { catch(this, value) }
is Right -> this@catch
}
}

@JvmName("catchReified")
@OptIn(ExperimentalTypeInference::class)
public inline fun <E, reified T : Throwable, A> Either<Throwable, A>.catch(@BuilderInference catch: Raise<E>.(T) -> A): Either<E, A> {
contract { callsInPlace(catch, InvocationKind.AT_MOST_ONCE) }
return catch { e -> if (e is T) catch(e) else throw e }
return recover { e -> if (e is T) catch(e) else throw e }
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ import kotlin.jvm.JvmName
* <!--- KNIT example-effect-error-01.kt -->
*/
public infix fun <E, E2, A> Effect<E, A>.recover(@BuilderInference resolve: suspend Raise<E2>.(raised: E) -> A): Effect<E2, A> =
effect { recover(resolve) }
effect { recover({ invoke() }) { resolve(it) } }

/**
* Catch any unexpected exceptions, and [resolve] them.
* Catch any unexpected exceptions, and [recover] them.
* You can either return a value a new value of [A],
* or short-circuit the effect by raising with a value of [E],
* or raise an exception into [suspend].
Expand All @@ -53,8 +53,8 @@ public infix fun <E, E2, A> Effect<E, A>.recover(@BuilderInference resolve: susp
* ```
* <!--- KNIT example-effect-error-02.kt -->
*/
public infix fun <E, A> Effect<E, A>.catch(@BuilderInference resolve: suspend Raise<E>.(throwable: Throwable) -> A): Effect<E, A> =
effect { catch(resolve) }
public infix fun <E, A> Effect<E, A>.catch(@BuilderInference recover: suspend Raise<E>.(throwable: Throwable) -> A): Effect<E, A> =
effect { catch({ invoke() }) { recover(it) } }

/**
* A version of [catch] that refines the [Throwable] to [T].
Expand Down Expand Up @@ -85,32 +85,28 @@ public infix fun <E, A> Effect<E, A>.catch(@BuilderInference resolve: suspend Ra
public inline infix fun <reified T : Throwable, E, A> Effect<E, A>.catch(
@BuilderInference crossinline recover: suspend Raise<E>.(T) -> A,
): Effect<E, A> =
effect { catch { t: Throwable -> if (t is T) recover(t) else throw t } }
effect { catch({ invoke() }) { t: T -> recover(t) } }

/** Runs the [Effect] and captures any [nonFatalOrThrow] exception into [Result]. */
public fun <E, A> Effect<E, A>.catch(): Effect<E, Result<A>> =
effect {
try {
Result.success(invoke())
} catch (e: Throwable) {
Result.failure(e.nonFatalOrThrow())
}
catch({ Result.success(invoke()) }, Result.Companion::failure)
}

public suspend inline infix fun <E, A> Effect<E, A>.getOrElse(onRaise: suspend (E) -> A): A =
recover({ invoke() }) { onRaise(it) }

public infix fun <E, E2, A> EagerEffect<E, A>.recover(@BuilderInference resolve: Raise<E2>.(raised: E) -> A): EagerEffect<E2, A> =
eagerEffect { recover(resolve) }
eagerEffect { recover({ invoke() }) { resolve(it) } }

public infix fun <E, A> EagerEffect<E, A>.catch(@BuilderInference recover: Raise<E>.(throwable: Throwable) -> A): EagerEffect<E, A> =
eagerEffect { catch(recover) }
eagerEffect { catch({ invoke() }) { recover(it) } }

@JvmName("catchReified")
public inline infix fun <reified T : Throwable, E, A> EagerEffect<E, A>.catch(
@BuilderInference crossinline recover: Raise<E>.(T) -> A,
): EagerEffect<E, A> =
eagerEffect { catch { t: Throwable -> if (t is T) recover(t) else throw t } }

public suspend inline infix fun <E, A> Effect<E, A>.getOrElse(onRaise: (E) -> A): A =
recover({ invoke() }, onRaise)
eagerEffect { catch({ invoke() }) { t: T -> recover(t) } }

public inline infix fun <E, A> EagerEffect<E, A>.getOrElse(onRaise: (E) -> A): A =
recover({ invoke() }, onRaise)
Loading