Skip to main content

CanCatch

CanCatch#

CanCatch lets you catch NonFatal Throwable in the F[A] and turned it into F[Either[Throwable, A]]. It takes a function from Throwable to your own error type, yet it can handle only NonFatal ones as already mentioned.

trait CanCatch[F[_]] {  def catchNonFatal[A, B](fb: => F[B])(f: Throwable => A): F[Either[A, B]]
  def catchNonFatalEither[A, B](fab: => F[Either[A, B]])(f: Throwable => A): F[Either[A, B]]
  def catchNonFatalEitherT[A, B](fab: => EitherT[F, A, B])(f: Throwable => A): EitherT[F, A, B]}

CanCatch.catchNonFatal#

CanCatch[F].catchNonFatal[A, B] lets you catch NonFatal Throwable from F[B] and returns F[Either[A, B]].

How to Use#

import cats.effect._
import effectie.cats._
val fa = CanCatch[IO].catchNonFatal(    IO(throw new RuntimeException("Something's wrong!"))  )(identity)// fa: IO[Either[Throwable, Nothing]] = Map(//   source = Bind(//     source = Delay(thunk = <function0>),//     f = <function1>,//     trace = null//   ),//   f = effectie.cats.CanCatch$$anon$1$$Lambda$10872/0x0000000103442040@25c7522e,//   trace = StackTrace(//     stackTrace = List(//       cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//       cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//       cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//       cats.effect.IO.map(IO.scala:106),//       effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//       effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//       repl.MdocSession$App0$.<clinit>(can-catch.md:19),//       repl.MdocSession$App.<init>(can-catch.md:5),//       repl.MdocSession$.app(can-catch.md:3),//       mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//       scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//       scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//       scala.Console$.withErr(Console.scala:193),//       mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//       scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//       scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//       scala.Console$.withOut(Console.scala:164),//       mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//       mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//       mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//     )//   )// )
fa.unsafeRunSync()// res1: Either[Throwable, Nothing] = Left(//   value = java.lang.RuntimeException: Something's wrong!// )

Happy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)}
def doSomethingBad(n: Int): Int =  if (n < 0)    throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")  else    n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  CanCatch[F].catchNonFatal(    for {      a <- pureOf(n + 100)      b <- effectOf(doSomethingBad(a))    } yield b  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](1)// fa: IO[Either[MyError, Int]] = Map(//   source = Bind(//     source = Bind(//       source = Pure(a = 101),//       f = <function1>,//       trace = StackTrace(//         stackTrace = List(//           cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//           cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//           cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//           cats.effect.IO.flatMap(IO.scala:133),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//           cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//           cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//           cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//           repl.MdocSession$App6$$anonfun$doSomething$1.apply(can-catch.md:117),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//           repl.MdocSession$App6$.doSomething(can-catch.md:120),//           repl.MdocSession$App6$.<clinit>(can-catch.md:123),//           repl.MdocSession$App4$.<clinit>(can-catch.md:74),//           repl.MdocSession$App2$.<clinit>(can-catch.md:57),//           repl.MdocSession$App0$.<clinit>(can-catch.md:25),//           repl.MdocSession$App.<init>(can-catch.md:5),//           repl.MdocSession$.app(can-catch.md:3),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withErr(Console.scala:193),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withOut(Console.scala:164),//           mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//           mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//           mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//         )//       )//     ),//     f = <function1>,//     trace = null// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Right(value = 202)result match {  case Right(b) =>    println(s"Result is $b")  case Left(MyError.NonFatalThrowable(a)) =>    println(s"Result: Failed with $a")}// Result is 202

Unhappy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)}
def doSomethingBad(n: Int): Int =if (n < 0)  throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")else  n * 2

def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  CanCatch[F].catchNonFatal(    for {      a <- pureOf(n + 100)      b <- effectOf(doSomethingBad(a))    } yield b  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](-101)// fa: IO[Either[MyError, Int]] = Map(//   source = Bind(//     source = Bind(//       source = Pure(a = -1),//       f = <function1>,//       trace = StackTrace(//         stackTrace = List(//           cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//           cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//           cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//           cats.effect.IO.flatMap(IO.scala:133),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//           cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//           cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//           cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//           repl.MdocSession$App12$$anonfun$doSomething$7.apply(can-catch.md:317),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//           repl.MdocSession$App12$.doSomething(can-catch.md:320),//           repl.MdocSession$App12$.<clinit>(can-catch.md:323),//           repl.MdocSession$App10$.<clinit>(can-catch.md:274),//           repl.MdocSession$App8$.<clinit>(can-catch.md:217),//           repl.MdocSession$App6$.<clinit>(can-catch.md:137),//           repl.MdocSession$App4$.<clinit>(can-catch.md:74),//           repl.MdocSession$App2$.<clinit>(can-catch.md:57),//           repl.MdocSession$App0$.<clinit>(can-catch.md:25),//           repl.MdocSession$App.<init>(can-catch.md:5),//           repl.MdocSession$.app(can-catch.md:3),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withErr(Console.scala:193),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withOut(Console.scala:164),//           mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//           mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//           mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//         )//       )// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Left(//   value = NonFatalThrowable(//     throwable = java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1]//   )// )result match {  case Right(b) =>    println(s"Result is $b")  case Left(MyError.NonFatalThrowable(a)) =>    println(s"Result: Failed with $a")}// Result: Failed with java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1]

CanCatch.catchNonFatalEither#

CanCatch[F].catchNonFatalEither[A, B] lets you catch NonFatal Throwable from F[Either[A, B]] and returns F[Either[A, B]].

How to Use#

import cats.effect._
import effectie.cats._
val fa = CanCatch[IO].catchNonFatalEither(    IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int])  )(identity)// fa: IO[Either[Throwable, Int]] = Map(//   source = Map(//     source = Bind(//       source = Delay(thunk = <function0>),//       f = <function1>,//       trace = null//     ),//     f = effectie.cats.CanCatch$$anon$1$$Lambda$10872/0x0000000103442040@34ab9536,//     trace = StackTrace(//       stackTrace = List(//         cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//         cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//         cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//         cats.effect.IO.map(IO.scala:106),//         effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//         effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//         repl.MdocSession$App0$.<clinit>(can-catch.md:19),//         repl.MdocSession$App.<init>(can-catch.md:5),//         repl.MdocSession$.app(can-catch.md:3),//         mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//         scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//         scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//         scala.Console$.withErr(Console.scala:193),//         mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//         scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//         scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//         scala.Console$.withOut(Console.scala:164),//         mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//         mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//         mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//       )//     )//   ),//   f = effectie.cats.CanCatch$$anon$1$$Lambda$10891/0x000000010344b040@761dfe91,//   trace = StackTrace(//     stackTrace = List(//       cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//       cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//       cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//       cats.effect.IO.map(IO.scala:106),//       effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//       effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//       repl.MdocSession$App18$.<clinit>(can-catch.md:488),// ...
fa.unsafeRunSync()// res19: Either[Throwable, Int] = Left(//   value = java.lang.RuntimeException: Something's wrong!// )

Happy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =  if (n < 0)    throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")  else    n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  CanCatch[F].catchNonFatalEither(    for {      aOrB <- pureOf(divide100By(n))      c <- effectOf(aOrB.map(b => doSomethingBad(b)))    } yield c  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = 100)),//         f = <function1>,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//             cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//             cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//             repl.MdocSession$App24$$anonfun$doSomething$13.apply(can-catch.md:597),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             repl.MdocSession$App24$.doSomething(can-catch.md:600),//             repl.MdocSession$App24$.<clinit>(can-catch.md:603),//             repl.MdocSession$App22$.<clinit>(can-catch.md:543),//             repl.MdocSession$App20$.<clinit>(can-catch.md:526),//             repl.MdocSession$App18$.<clinit>(can-catch.md:494),//             repl.MdocSession$App16$.<clinit>(can-catch.md:474),//             repl.MdocSession$App14$.<clinit>(can-catch.md:417),//             repl.MdocSession$App12$.<clinit>(can-catch.md:337),//             repl.MdocSession$App10$.<clinit>(can-catch.md:274),//             repl.MdocSession$App8$.<clinit>(can-catch.md:217),//             repl.MdocSession$App6$.<clinit>(can-catch.md:137),//             repl.MdocSession$App4$.<clinit>(can-catch.md:74),//             repl.MdocSession$App2$.<clinit>(can-catch.md:57),//             repl.MdocSession$App0$.<clinit>(can-catch.md:25),//             repl.MdocSession$App.<init>(can-catch.md:5),//             repl.MdocSession$.app(can-catch.md:3),//             mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//             scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//             scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//             scala.Console$.withErr(Console.scala:193),//             mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Right(value = 200)result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result is 200

Unhappy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =if (n < 0)  throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")else  n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  CanCatch[F].catchNonFatalEither(    for {      aOrB <- pureOf(divide100By(n))      c <- effectOf(aOrB.map(b => doSomethingBad(b)))    } yield c  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](-1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = -100)),//         f = <function1>,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//             cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//             cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//             repl.MdocSession$App30$$anonfun$doSomething$19.apply(can-catch.md:829),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             repl.MdocSession$App30$.doSomething(can-catch.md:832),//             repl.MdocSession$App30$.<clinit>(can-catch.md:835),//             repl.MdocSession$App28$.<clinit>(can-catch.md:775),//             repl.MdocSession$App26$.<clinit>(can-catch.md:707),//             repl.MdocSession$App24$.<clinit>(can-catch.md:617),//             repl.MdocSession$App22$.<clinit>(can-catch.md:543),//             repl.MdocSession$App20$.<clinit>(can-catch.md:526),//             repl.MdocSession$App18$.<clinit>(can-catch.md:494),//             repl.MdocSession$App16$.<clinit>(can-catch.md:474),//             repl.MdocSession$App14$.<clinit>(can-catch.md:417),//             repl.MdocSession$App12$.<clinit>(can-catch.md:337),//             repl.MdocSession$App10$.<clinit>(can-catch.md:274),//             repl.MdocSession$App8$.<clinit>(can-catch.md:217),//             repl.MdocSession$App6$.<clinit>(can-catch.md:137),//             repl.MdocSession$App4$.<clinit>(can-catch.md:74),//             repl.MdocSession$App2$.<clinit>(can-catch.md:57),//             repl.MdocSession$App0$.<clinit>(can-catch.md:25),//             repl.MdocSession$App.<init>(can-catch.md:5),//             repl.MdocSession$.app(can-catch.md:3),//             mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//             scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//             scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Left(//   value = NonFatalThrowable(//     throwable = java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]//   )// )result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])

CanCatch.catchNonFatalEitherT#

CanCatch[F].catchNonFatalEitherT[A, B] lets you catch NonFatal Throwable from EitherT[F, A, B] and returns EitherT[F, A, B].

How to Use#

import cats.data.EitherTimport cats.effect._
import effectie.cats._
val fa = CanCatch[IO].catchNonFatalEitherT(    EitherT(IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int]))  )(identity)// fa: EitherT[IO, Throwable, Int] = EitherT(//   value = Map(//     source = Map(//       source = Bind(//         source = Delay(thunk = <function0>),//         f = <function1>,//         trace = null//       ),//       f = effectie.cats.CanCatch$$anon$1$$Lambda$10872/0x0000000103442040@dc9f8f4,//       trace = StackTrace(//         stackTrace = List(//           cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//           cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//           cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//           cats.effect.IO.map(IO.scala:106),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//           repl.MdocSession$App0$.<clinit>(can-catch.md:19),//           repl.MdocSession$App.<init>(can-catch.md:5),//           repl.MdocSession$.app(can-catch.md:3),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withErr(Console.scala:193),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withOut(Console.scala:164),//           mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//           mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//           mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//         )//       )//     ),//     f = effectie.cats.CanCatch$$anon$1$$Lambda$10891/0x000000010344b040@761dfe91,//     trace = StackTrace(//       stackTrace = List(//         cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//         cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//         cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),// ...
fa.value.unsafeRunSync()// res37: Either[Throwable, Int] = Left(//   value = java.lang.RuntimeException: Something's wrong!// )

Happy Path Example#

import cats._import cats.syntax.all._import cats.data.EitherTimport cats.effect._
import effectie.cats._import effectie.cats.Effectful._import effectie.cats.EitherTSupport._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =  if (n < 0)    throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")  else    n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  CanCatch[F].catchNonFatalEitherT(    for {      b <- EitherT(pureOf(divide100By(n)))      c <- eitherTRight[MyError](doSomethingBad(b))    } yield c  )(MyError.nonFatalThrowable).value
val fa = doSomething[IO](1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = 100)),//         f = cats.data.EitherT$$Lambda$10894/0x0000000103449840@48c21ee5,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.data.EitherT.flatMap(EitherT.scala:403),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             effectie.cats.CanCatch.$anonfun$catchNonFatalEitherT$1(CanCatch.scala:18),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             effectie.cats.CanCatch.catchNonFatalEitherT(CanCatch.scala:18),//             effectie.cats.CanCatch.catchNonFatalEitherT$(CanCatch.scala:17),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEitherT(CanCatch.scala:24),//             repl.MdocSession$App42$.doSomething(can-catch.md:1151),//             repl.MdocSession$App42$.<clinit>(can-catch.md:1154),//             repl.MdocSession$App40$.<clinit>(can-catch.md:1088),//             repl.MdocSession$App38$.<clinit>(can-catch.md:1065),//             repl.MdocSession$App36$.<clinit>(can-catch.md:1030),//             repl.MdocSession$App34$.<clinit>(can-catch.md:1007),//             repl.MdocSession$App32$.<clinit>(can-catch.md:939),//             repl.MdocSession$App30$.<clinit>(can-catch.md:849),//             repl.MdocSession$App28$.<clinit>(can-catch.md:775),//             repl.MdocSession$App26$.<clinit>(can-catch.md:707),//             repl.MdocSession$App24$.<clinit>(can-catch.md:617),//             repl.MdocSession$App22$.<clinit>(can-catch.md:543),//             repl.MdocSession$App20$.<clinit>(can-catch.md:526),//             repl.MdocSession$App18$.<clinit>(can-catch.md:494),//             repl.MdocSession$App16$.<clinit>(can-catch.md:474),//             repl.MdocSession$App14$.<clinit>(can-catch.md:417),//             repl.MdocSession$App12$.<clinit>(can-catch.md:337),//             repl.MdocSession$App10$.<clinit>(can-catch.md:274),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Right(value = 200)result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result is 200

Unhappy Path Example#

import cats._import cats.data.EitherTimport cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.EitherTSupport._import effectie.cats.Effectful._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =if (n < 0)  throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")else  n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  CanCatch[F].catchNonFatalEitherT(    for {      b <- EitherT(pureOf(divide100By(n)))      c <- eitherTRight[MyError](doSomethingBad(b))    } yield c  )(MyError.nonFatalThrowable).value
val fa = doSomething[IO](-1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = -100)),//         f = cats.data.EitherT$$Lambda$10894/0x0000000103449840@4fa5e9d3,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.data.EitherT.flatMap(EitherT.scala:403),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             effectie.cats.CanCatch.$anonfun$catchNonFatalEitherT$1(CanCatch.scala:18),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             effectie.cats.CanCatch.catchNonFatalEitherT(CanCatch.scala:18),//             effectie.cats.CanCatch.catchNonFatalEitherT$(CanCatch.scala:17),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEitherT(CanCatch.scala:24),//             repl.MdocSession$App42$.doSomething(can-catch.md:1151),//             repl.MdocSession$App42$.<clinit>(can-catch.md:1154),//             repl.MdocSession$App40$.<clinit>(can-catch.md:1088),//             repl.MdocSession$App38$.<clinit>(can-catch.md:1065),//             repl.MdocSession$App36$.<clinit>(can-catch.md:1030),//             repl.MdocSession$App34$.<clinit>(can-catch.md:1007),//             repl.MdocSession$App32$.<clinit>(can-catch.md:939),//             repl.MdocSession$App30$.<clinit>(can-catch.md:849),//             repl.MdocSession$App28$.<clinit>(can-catch.md:775),//             repl.MdocSession$App26$.<clinit>(can-catch.md:707),//             repl.MdocSession$App24$.<clinit>(can-catch.md:617),//             repl.MdocSession$App22$.<clinit>(can-catch.md:543),//             repl.MdocSession$App20$.<clinit>(can-catch.md:526),//             repl.MdocSession$App18$.<clinit>(can-catch.md:494),//             repl.MdocSession$App16$.<clinit>(can-catch.md:474),//             repl.MdocSession$App14$.<clinit>(can-catch.md:417),//             repl.MdocSession$App12$.<clinit>(can-catch.md:337),//             repl.MdocSession$App10$.<clinit>(can-catch.md:274),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Left(//   value = NonFatalThrowable(//     throwable = java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]//   )// )result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])

Catching#

Catching.catchNonFatal provides a convenient way to use CanCatch to catch NonFatal Throwable in the F[A] and turned it into F[Either[Throwable, A]]. Just like CanCatch, it takes a function from Throwable to your own error type, yet it can handle only NonFatal ones as already mentioned.

Catching.catchNonFatal#

catchNonFatal lets you catch NonFatal Throwable from F[B] and returns F[Either[A, B]].

How to Use#

import cats.effect._
import effectie.cats.Catching._
val fa = catchNonFatal(    IO(throw new RuntimeException("Something's wrong!"))  )(identity)// fa: IO[Either[Throwable, Nothing]] = Map(//   source = Bind(//     source = Delay(thunk = <function0>),//     f = <function1>,//     trace = null//   ),//   f = effectie.cats.CanCatch$$anon$1$$Lambda$10872/0x0000000103442040@41f45da9,//   trace = StackTrace(//     stackTrace = List(//       cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//       cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//       cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//       cats.effect.IO.map(IO.scala:106),//       effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//       effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//       repl.MdocSession$App0$.<clinit>(can-catch.md:19),//       repl.MdocSession$App.<init>(can-catch.md:5),//       repl.MdocSession$.app(can-catch.md:3),//       mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//       scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//       scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//       scala.Console$.withErr(Console.scala:193),//       mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//       scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//       scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//       scala.Console$.withOut(Console.scala:164),//       mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//       mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//       mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//     )//   )// )
fa.unsafeRunSync()// res55: Either[Throwable, Nothing] = Left(//   value = java.lang.RuntimeException: Something's wrong!// )

Happy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._import effectie.cats.Catching._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)}
def doSomethingBad(n: Int): Int =  if (n < 0)    throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")  else    n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  catchNonFatal(    for {      a <- pureOf(n + 100)      b <- effectOf(doSomethingBad(a))    } yield b  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](1)// fa: IO[Either[MyError, Int]] = Map(//   source = Bind(//     source = Bind(//       source = Pure(a = 101),//       f = <function1>,//       trace = StackTrace(//         stackTrace = List(//           cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//           cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//           cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//           cats.effect.IO.flatMap(IO.scala:133),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//           cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//           cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//           cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//           repl.MdocSession$App60$$anonfun$doSomething$37.apply(can-catch.md:1703),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//           effectie.cats.Catching$CurriedCanCatch2$.apply$extension(Catching.scala:45),//           repl.MdocSession$App60$.doSomething(can-catch.md:1706),//           repl.MdocSession$App60$.<clinit>(can-catch.md:1709),//           repl.MdocSession$App58$.<clinit>(can-catch.md:1657),//           repl.MdocSession$App56$.<clinit>(can-catch.md:1640),//           repl.MdocSession$App54$.<clinit>(can-catch.md:1608),//           repl.MdocSession$App52$.<clinit>(can-catch.md:1588),//           repl.MdocSession$App50$.<clinit>(can-catch.md:1514),//           repl.MdocSession$App48$.<clinit>(can-catch.md:1418),//           repl.MdocSession$App46$.<clinit>(can-catch.md:1338),//           repl.MdocSession$App44$.<clinit>(can-catch.md:1264),//           repl.MdocSession$App42$.<clinit>(can-catch.md:1168),//           repl.MdocSession$App40$.<clinit>(can-catch.md:1088),//           repl.MdocSession$App38$.<clinit>(can-catch.md:1065),//           repl.MdocSession$App36$.<clinit>(can-catch.md:1030),//           repl.MdocSession$App34$.<clinit>(can-catch.md:1007),//           repl.MdocSession$App32$.<clinit>(can-catch.md:939),//           repl.MdocSession$App30$.<clinit>(can-catch.md:849),//           repl.MdocSession$App28$.<clinit>(can-catch.md:775),//           repl.MdocSession$App26$.<clinit>(can-catch.md:707),//           repl.MdocSession$App24$.<clinit>(can-catch.md:617),//           repl.MdocSession$App22$.<clinit>(can-catch.md:543),//           repl.MdocSession$App20$.<clinit>(can-catch.md:526),//           repl.MdocSession$App18$.<clinit>(can-catch.md:494),//           repl.MdocSession$App16$.<clinit>(can-catch.md:474),//           repl.MdocSession$App14$.<clinit>(can-catch.md:417),//           repl.MdocSession$App12$.<clinit>(can-catch.md:337),//           repl.MdocSession$App10$.<clinit>(can-catch.md:274),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Right(value = 202)result match {  case Right(b) =>    println(s"Result is $b")  case Left(MyError.NonFatalThrowable(a)) =>    println(s"Result: Failed with $a")}// Result is 202

Unhappy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._import effectie.cats.Catching._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)}
def doSomethingBad(n: Int): Int =if (n < 0)  throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")else  n * 2

def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  catchNonFatal(    for {      a <- pureOf(n + 100)      b <- effectOf(doSomethingBad(a))    } yield b  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](-101)// fa: IO[Either[MyError, Int]] = Map(//   source = Bind(//     source = Bind(//       source = Pure(a = -1),//       f = <function1>,//       trace = StackTrace(//         stackTrace = List(//           cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//           cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//           cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//           cats.effect.IO.flatMap(IO.scala:133),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//           cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//           cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//           cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//           cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//           repl.MdocSession$App66$$anonfun$doSomething$43.apply(can-catch.md:1913),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//           effectie.cats.Catching$CurriedCanCatch2$.apply$extension(Catching.scala:45),//           repl.MdocSession$App66$.doSomething(can-catch.md:1916),//           repl.MdocSession$App66$.<clinit>(can-catch.md:1919),//           repl.MdocSession$App64$.<clinit>(can-catch.md:1867),//           repl.MdocSession$App62$.<clinit>(can-catch.md:1807),//           repl.MdocSession$App60$.<clinit>(can-catch.md:1723),//           repl.MdocSession$App58$.<clinit>(can-catch.md:1657),//           repl.MdocSession$App56$.<clinit>(can-catch.md:1640),//           repl.MdocSession$App54$.<clinit>(can-catch.md:1608),//           repl.MdocSession$App52$.<clinit>(can-catch.md:1588),//           repl.MdocSession$App50$.<clinit>(can-catch.md:1514),//           repl.MdocSession$App48$.<clinit>(can-catch.md:1418),//           repl.MdocSession$App46$.<clinit>(can-catch.md:1338),//           repl.MdocSession$App44$.<clinit>(can-catch.md:1264),//           repl.MdocSession$App42$.<clinit>(can-catch.md:1168),//           repl.MdocSession$App40$.<clinit>(can-catch.md:1088),//           repl.MdocSession$App38$.<clinit>(can-catch.md:1065),//           repl.MdocSession$App36$.<clinit>(can-catch.md:1030),//           repl.MdocSession$App34$.<clinit>(can-catch.md:1007),//           repl.MdocSession$App32$.<clinit>(can-catch.md:939),//           repl.MdocSession$App30$.<clinit>(can-catch.md:849),//           repl.MdocSession$App28$.<clinit>(can-catch.md:775),//           repl.MdocSession$App26$.<clinit>(can-catch.md:707),//           repl.MdocSession$App24$.<clinit>(can-catch.md:617),//           repl.MdocSession$App22$.<clinit>(can-catch.md:543),//           repl.MdocSession$App20$.<clinit>(can-catch.md:526),//           repl.MdocSession$App18$.<clinit>(can-catch.md:494),//           repl.MdocSession$App16$.<clinit>(can-catch.md:474),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Left(//   value = NonFatalThrowable(//     throwable = java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1]//   )// )result match {  case Right(b) =>    println(s"Result is $b")  case Left(MyError.NonFatalThrowable(a)) =>    println(s"Result: Failed with $a")}// Result: Failed with java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1]

Catching.catchNonFatalEither#

Catching.catchNonFatalEither provides a convenient way to use CanCatch to catch NonFatal Throwable from F[Either[A, B]] and returns F[Either[A, B]].

How to Use#

import cats.effect._
import effectie.cats.Catching._
val fa = catchNonFatalEither(    IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int])  )(identity)// fa: IO[Either[Throwable, Int]] = Map(//   source = Map(//     source = Bind(//       source = Delay(thunk = <function0>),//       f = <function1>,//       trace = null//     ),//     f = effectie.cats.CanCatch$$anon$1$$Lambda$10872/0x0000000103442040@422009c2,//     trace = StackTrace(//       stackTrace = List(//         cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//         cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//         cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//         cats.effect.IO.map(IO.scala:106),//         effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//         effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//         repl.MdocSession$App0$.<clinit>(can-catch.md:19),//         repl.MdocSession$App.<init>(can-catch.md:5),//         repl.MdocSession$.app(can-catch.md:3),//         mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//         scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//         scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//         scala.Console$.withErr(Console.scala:193),//         mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//         scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//         scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//         scala.Console$.withOut(Console.scala:164),//         mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//         mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//         mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//       )//     )//   ),//   f = effectie.cats.CanCatch$$anon$1$$Lambda$10891/0x000000010344b040@761dfe91,//   trace = StackTrace(//     stackTrace = List(//       cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//       cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//       cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//       cats.effect.IO.map(IO.scala:106),//       effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//       effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//       repl.MdocSession$App18$.<clinit>(can-catch.md:488),// ...
fa.unsafeRunSync()// res73: Either[Throwable, Int] = Left(//   value = java.lang.RuntimeException: Something's wrong!// )

Happy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._import effectie.cats.Catching._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =  if (n < 0)    throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")  else    n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  catchNonFatalEither(    for {      aOrB <- pureOf(divide100By(n))      c <- effectOf(aOrB.map(b => doSomethingBad(b)))    } yield c  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = 100)),//         f = <function1>,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//             cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//             cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//             repl.MdocSession$App78$$anonfun$doSomething$49.apply(can-catch.md:2202),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             effectie.cats.Catching$CurriedCanCatchEither2$.apply$extension(Catching.scala:74),//             repl.MdocSession$App78$.doSomething(can-catch.md:2205),//             repl.MdocSession$App78$.<clinit>(can-catch.md:2208),//             repl.MdocSession$App76$.<clinit>(can-catch.md:2145),//             repl.MdocSession$App74$.<clinit>(can-catch.md:2128),//             repl.MdocSession$App72$.<clinit>(can-catch.md:2096),//             repl.MdocSession$App70$.<clinit>(can-catch.md:2076),//             repl.MdocSession$App68$.<clinit>(can-catch.md:2016),//             repl.MdocSession$App66$.<clinit>(can-catch.md:1933),//             repl.MdocSession$App64$.<clinit>(can-catch.md:1867),//             repl.MdocSession$App62$.<clinit>(can-catch.md:1807),//             repl.MdocSession$App60$.<clinit>(can-catch.md:1723),//             repl.MdocSession$App58$.<clinit>(can-catch.md:1657),//             repl.MdocSession$App56$.<clinit>(can-catch.md:1640),//             repl.MdocSession$App54$.<clinit>(can-catch.md:1608),//             repl.MdocSession$App52$.<clinit>(can-catch.md:1588),//             repl.MdocSession$App50$.<clinit>(can-catch.md:1514),//             repl.MdocSession$App48$.<clinit>(can-catch.md:1418),//             repl.MdocSession$App46$.<clinit>(can-catch.md:1338),//             repl.MdocSession$App44$.<clinit>(can-catch.md:1264),//             repl.MdocSession$App42$.<clinit>(can-catch.md:1168),//             repl.MdocSession$App40$.<clinit>(can-catch.md:1088),//             repl.MdocSession$App38$.<clinit>(can-catch.md:1065),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Right(value = 200)result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result is 200

Unhappy Path Example#

import cats._import cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._import effectie.cats.Catching._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =if (n < 0)  throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")else  n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  catchNonFatalEither(    for {      aOrB <- pureOf(divide100By(n))      c <- effectOf(aOrB.map(b => doSomethingBad(b)))    } yield c  )(MyError.nonFatalThrowable)
val fa = doSomething[IO](-1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = -100)),//         f = <function1>,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.FlatMap$Ops.flatMap(FlatMap.scala:229),//             cats.FlatMap$Ops.flatMap$(FlatMap.scala:229),//             cats.FlatMap$ToFlatMapOps$$anon$2.flatMap(FlatMap.scala:243),//             repl.MdocSession$App84$$anonfun$doSomething$55.apply(can-catch.md:2443),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             effectie.cats.Catching$CurriedCanCatchEither2$.apply$extension(Catching.scala:74),//             repl.MdocSession$App84$.doSomething(can-catch.md:2446),//             repl.MdocSession$App84$.<clinit>(can-catch.md:2449),//             repl.MdocSession$App82$.<clinit>(can-catch.md:2386),//             repl.MdocSession$App80$.<clinit>(can-catch.md:2315),//             repl.MdocSession$App78$.<clinit>(can-catch.md:2222),//             repl.MdocSession$App76$.<clinit>(can-catch.md:2145),//             repl.MdocSession$App74$.<clinit>(can-catch.md:2128),//             repl.MdocSession$App72$.<clinit>(can-catch.md:2096),//             repl.MdocSession$App70$.<clinit>(can-catch.md:2076),//             repl.MdocSession$App68$.<clinit>(can-catch.md:2016),//             repl.MdocSession$App66$.<clinit>(can-catch.md:1933),//             repl.MdocSession$App64$.<clinit>(can-catch.md:1867),//             repl.MdocSession$App62$.<clinit>(can-catch.md:1807),//             repl.MdocSession$App60$.<clinit>(can-catch.md:1723),//             repl.MdocSession$App58$.<clinit>(can-catch.md:1657),//             repl.MdocSession$App56$.<clinit>(can-catch.md:1640),//             repl.MdocSession$App54$.<clinit>(can-catch.md:1608),//             repl.MdocSession$App52$.<clinit>(can-catch.md:1588),//             repl.MdocSession$App50$.<clinit>(can-catch.md:1514),//             repl.MdocSession$App48$.<clinit>(can-catch.md:1418),//             repl.MdocSession$App46$.<clinit>(can-catch.md:1338),//             repl.MdocSession$App44$.<clinit>(can-catch.md:1264),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Left(//   value = NonFatalThrowable(//     throwable = java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]//   )// )result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])

Catching.catchNonFatalEitherT#

Catching.catchNonFatalEitherT provides a convenient way to use CanCatch to catch NonFatal Throwable from EitherT[F, A, B] and returns EitherT[F, A, B].

How to Use#

import cats.data.EitherTimport cats.effect._
import effectie.cats.Catching._
val fa = catchNonFatalEitherT[IO](    EitherT(IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int]))  )(identity)// fa: EitherT[IO, Throwable, Int] = EitherT(//   value = Map(//     source = Map(//       source = Bind(//         source = Delay(thunk = <function0>),//         f = <function1>,//         trace = null//       ),//       f = effectie.cats.CanCatch$$anon$1$$Lambda$10872/0x0000000103442040@77b6646,//       trace = StackTrace(//         stackTrace = List(//           cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//           cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//           cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//           cats.effect.IO.map(IO.scala:106),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//           effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:24),//           repl.MdocSession$App0$.<clinit>(can-catch.md:19),//           repl.MdocSession$App.<init>(can-catch.md:5),//           repl.MdocSession$.app(can-catch.md:3),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$2(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withErr(Console.scala:193),//           mdoc.internal.document.DocumentBuilder$$doc$.$anonfun$build$1(DocumentBuilder.scala:89),//           scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18),//           scala.util.DynamicVariable.withValue(DynamicVariable.scala:59),//           scala.Console$.withOut(Console.scala:164),//           mdoc.internal.document.DocumentBuilder$$doc$.build(DocumentBuilder.scala:88),//           mdoc.internal.markdown.MarkdownBuilder$.$anonfun$buildDocument$2(MarkdownBuilder.scala:47),//           mdoc.internal.markdown.MarkdownBuilder$$anon$1.run(MarkdownBuilder.scala:103)//         )//       )//     ),//     f = effectie.cats.CanCatch$$anon$1$$Lambda$10891/0x000000010344b040@761dfe91,//     trace = StackTrace(//       stackTrace = List(//         cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//         cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//         cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),// ...
fa.value.unsafeRunSync()// res91: Either[Throwable, Int] = Left(//   value = java.lang.RuntimeException: Something's wrong!// )

Happy Path Example#

import cats._import cats.syntax.all._import cats.data.EitherTimport cats.effect._
import effectie.cats._import effectie.cats.Effectful._import effectie.cats.Catching._import effectie.cats.EitherTSupport._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =  if (n < 0)    throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")  else    n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  catchNonFatalEitherT(    for {      b <- EitherT(pureOf(divide100By(n)))      c <- eitherTRight[MyError](doSomethingBad(b))    } yield c  )(MyError.nonFatalThrowable).value
val fa = doSomething[IO](1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = 100)),//         f = cats.data.EitherT$$Lambda$10894/0x0000000103449840@78ee34f2,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.data.EitherT.flatMap(EitherT.scala:403),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             effectie.cats.CanCatch.$anonfun$catchNonFatalEitherT$1(CanCatch.scala:18),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             effectie.cats.CanCatch.catchNonFatalEitherT(CanCatch.scala:18),//             effectie.cats.CanCatch.catchNonFatalEitherT$(CanCatch.scala:17),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEitherT(CanCatch.scala:24),//             repl.MdocSession$App42$.doSomething(can-catch.md:1151),//             repl.MdocSession$App42$.<clinit>(can-catch.md:1154),//             repl.MdocSession$App40$.<clinit>(can-catch.md:1088),//             repl.MdocSession$App38$.<clinit>(can-catch.md:1065),//             repl.MdocSession$App36$.<clinit>(can-catch.md:1030),//             repl.MdocSession$App34$.<clinit>(can-catch.md:1007),//             repl.MdocSession$App32$.<clinit>(can-catch.md:939),//             repl.MdocSession$App30$.<clinit>(can-catch.md:849),//             repl.MdocSession$App28$.<clinit>(can-catch.md:775),//             repl.MdocSession$App26$.<clinit>(can-catch.md:707),//             repl.MdocSession$App24$.<clinit>(can-catch.md:617),//             repl.MdocSession$App22$.<clinit>(can-catch.md:543),//             repl.MdocSession$App20$.<clinit>(can-catch.md:526),//             repl.MdocSession$App18$.<clinit>(can-catch.md:494),//             repl.MdocSession$App16$.<clinit>(can-catch.md:474),//             repl.MdocSession$App14$.<clinit>(can-catch.md:417),//             repl.MdocSession$App12$.<clinit>(can-catch.md:337),//             repl.MdocSession$App10$.<clinit>(can-catch.md:274),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Right(value = 200)result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result is 200

Unhappy Path Example#

import cats._import cats.data.EitherTimport cats.syntax.all._import cats.effect._
import effectie.cats._import effectie.cats.Effectful._import effectie.cats.Catching._import effectie.cats.EitherTSupport._
sealed trait MyErrorobject MyError {  final case class NonFatalThrowable(throwable: Throwable) extends MyError  case object DivideByZero extends MyError    def nonFatalThrowable(throwable: Throwable): MyError    = NonFatalThrowable(throwable)
  def divideByZero: MyError = DivideByZero}
def divide100By(n: Int): Either[MyError, Int] =  if (n === 0)    MyError.divideByZero.asLeft[Int]  else    (100 / n).asRight[MyError]
def doSomethingBad(n: Int): Int =if (n < 0)  throw new IllegalArgumentException(s"n cannot be a negative number. [n: $n]")else  n * 2
def doSomething[F[_]: Fx: CanCatch: Monad](  n: Int): F[Either[MyError, Int]] =  catchNonFatalEitherT(    for {      b <- EitherT(pureOf(divide100By(n)))      c <- eitherTRight[MyError](doSomethingBad(b))    } yield c  )(MyError.nonFatalThrowable).value
val fa = doSomething[IO](-1)// fa: IO[Either[MyError, Int]] = Map(//   source = Map(//     source = Bind(//       source = Bind(//         source = Pure(a = Right(value = -100)),//         f = cats.data.EitherT$$Lambda$10894/0x0000000103449840@65e98c1b,//         trace = StackTrace(//           stackTrace = List(//             cats.effect.internals.IOTracing$.buildFrame(IOTracing.scala:48),//             cats.effect.internals.IOTracing$.buildCachedFrame(IOTracing.scala:39),//             cats.effect.internals.IOTracing$.cached(IOTracing.scala:34),//             cats.effect.IO.flatMap(IO.scala:133),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:886),//             cats.effect.IOLowPriorityInstances$IOEffect.flatMap(IO.scala:863),//             cats.data.EitherT.flatMap(EitherT.scala:403),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             repl.MdocSession$App42$$anonfun$doSomething$25.apply(can-catch.md:1148),//             effectie.cats.CanCatch.$anonfun$catchNonFatalEitherT$1(CanCatch.scala:18),//             effectie.cats.CanCatch$$anon$1.catchNonFatal(CanCatch.scala:26),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:29),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEither(CanCatch.scala:24),//             effectie.cats.CanCatch.catchNonFatalEitherT(CanCatch.scala:18),//             effectie.cats.CanCatch.catchNonFatalEitherT$(CanCatch.scala:17),//             effectie.cats.CanCatch$$anon$1.catchNonFatalEitherT(CanCatch.scala:24),//             repl.MdocSession$App42$.doSomething(can-catch.md:1151),//             repl.MdocSession$App42$.<clinit>(can-catch.md:1154),//             repl.MdocSession$App40$.<clinit>(can-catch.md:1088),//             repl.MdocSession$App38$.<clinit>(can-catch.md:1065),//             repl.MdocSession$App36$.<clinit>(can-catch.md:1030),//             repl.MdocSession$App34$.<clinit>(can-catch.md:1007),//             repl.MdocSession$App32$.<clinit>(can-catch.md:939),//             repl.MdocSession$App30$.<clinit>(can-catch.md:849),//             repl.MdocSession$App28$.<clinit>(can-catch.md:775),//             repl.MdocSession$App26$.<clinit>(can-catch.md:707),//             repl.MdocSession$App24$.<clinit>(can-catch.md:617),//             repl.MdocSession$App22$.<clinit>(can-catch.md:543),//             repl.MdocSession$App20$.<clinit>(can-catch.md:526),//             repl.MdocSession$App18$.<clinit>(can-catch.md:494),//             repl.MdocSession$App16$.<clinit>(can-catch.md:474),//             repl.MdocSession$App14$.<clinit>(can-catch.md:417),//             repl.MdocSession$App12$.<clinit>(can-catch.md:337),//             repl.MdocSession$App10$.<clinit>(can-catch.md:274),// ...val result = fa.unsafeRunSync()// result: Either[MyError, Int] = Left(//   value = NonFatalThrowable(//     throwable = java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]//   )// )result match {  case Right(b) =>    println(s"Result is $b")  case Left(a) =>    println(s"Result: Failed with $a")}// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])