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

orDie combinator #902

Merged
merged 15 commits into from
Dec 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Fixed readme
  • Loading branch information
johnhungerford committed Dec 5, 2024
commit d417f0d9e8f83ac0992cbf7d7cb15938ab798b7a
42 changes: 29 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3381,7 +3381,7 @@ For each of these, to handle the effect, lifting the result type to `Result`, `S
Some examples:

```scala
val abortEffect: Int < Abort[String] = ???
val abortEffect: Int < Abort[String] = 1

// Converts failures to empty failure
val maybeEffect: Int < Abort[Absent] = abortEffect.abortToEmpty
Expand All @@ -3396,48 +3396,64 @@ val newAbortEffect: Int < Abort[Throwable] = choiceEffect.choiceToThrowable
To swallow errors à la ZIO's `orDie` method, you have choices:

```scala
val throwableAbort: Int < Abort[IOException] = ???
import kyo.*
import java.io.IOException

trait ErrorType

val throwableAbort: Int < Abort[IOException] = 1

// Throws a throwable Abort failure (will actually throw unless suspended)
val unsafeEffect: Int < Any = abortEffect.implicitThrowable
val unsafeEffect: Int < Any = throwableAbort.implicitThrowable

// Catch any suspended throws
val safeEffect: Int < Abort[Throwable] = unsafeEffect.explicitThrowable

val anyAbort: Int < Abort[ErrorType] = ???
val anyAbort: Int < Abort[ErrorType] = 1

// orDie will allow you to swallow *any* errors. If the error is throwable,
// orPanic will allow you to swallow *any* errors. If the error is throwable,
// it will throw that error. If not, it will wrap it in throwable PanicException type
val unsafeEffect: Int < Any = anyAbort.orDie
val unsafeAgain: Int < Any = anyAbort.orPanic
```

The Abort-specific error handling methods are as follows:

```scala
val effect: Int < Abort[A | B | C] = ???
import kyo.*

val handled: Result[A | B | C, Int] = effect.result
trait A
trait B
trait C

val effect: Int < Abort[A | B | C] = 1

val handled: Result[A | B | C, Int] < Any = effect.result
val caught: Int < Any = effect.catching(_.toString.size)
val partiallyCaught: Int < Abort[A | B | C] = effect.catchingSome { case err if err.toString.size > 5 => 0 }

// Manipulate single types from within the union
val handledA: Result[A, Int] < Abort[B | C] = effect.forAbort[A].result
val caughtA: Int < Abort[B | C] = effect.forAbort[A].catching(_.toString.size)
val partiallyCaughtA: Int < Abort[A | B | C] = effect.someAbort[A].catchingSome { case err if err.toString.size > 5 => 0 }
val partiallyCaughtA: Int < Abort[A | B | C] = effect.forAbort[A].catchingSome { case err if err.toString.size > 5 => 0 }
val aToEmpty: Int < Abort[Absent | B | C] = effect.forAbort[A].toEmpty
val aToChoice: Int < (Choice & Abort[B | C]) = effect.forAbort[A].toChoice
```

Note that `mapError` and similar ZIO methods are not really needed, as we can handle those cases with `catching` (and `forAbort[E].catching`):

```scala
val effect: Int < Abort[A] = ???
import kyo.*

trait A
trait B

val effect: Int < Abort[A] = 1

def fn(err: A): B = ???
def fn(err: A): B = new B {}
val mapped: Int < Abort[B] = effect.catching(err => Kyo.fail(fn(err)))

def impureFn(err: A): B < Async = ???
val mappedImpure: Int < (Abort[B] & Async) = effect.catching(err => fn(err).map(Kyo.fail))
def impureFn(err: A): B < Async = new B {}
val mappedImpure: Int < (Abort[B] & Async) = effect.catching(err => impureFn(err).map(Kyo.fail))
```

## Acknowledgements
Expand Down
21 changes: 21 additions & 0 deletions kyo-combinators/shared/src/main/scala/kyo/Combinators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,27 @@ class ForAbortOps[A, S, E, E1 <: E](effect: A < (Abort[E] & S)) extends AnyVal:
val handled = Abort.run[E1](effect.asInstanceOf[A < (Abort[E1 | ER] & S)])
handled.map((v: Result[E1, A]) => Abort.get(v.swap))
end swap

/** Catches partial Abort and panics instead
*
* @return
* A computation that panics instead of catching Abort effect failures
*/
def orPanic[ER](
using
ev: E => E1 | ER,
reduce: Reducible[Abort[ER]],
ct: SafeClassTag[E1],
fl: Flat[A],
frame: Frame
): A < (S & reduce.SReduced) =
Abort.run[E1](effect.asInstanceOf[A < (Abort[E1 | ER] & S)]).map:
case Result.Success(v) => v
case Result.Fail(thr: Throwable) => Abort.panic(thr).asInstanceOf[Nothing < Any]
case Result.Fail(other) => Abort.panic(PanicException(other)).asInstanceOf[Nothing < Any]
case other: Result.Panic => Abort.get(other).asInstanceOf[Nothing < Any]

end orPanic
end ForAbortOps

extension [A, S, E](effect: A < (S & Env[E]))
Expand Down
Loading