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.
CanCatch
looks like this.
trait CanCatch[F[_]] {
def catchNonFatal[A, B](fb: => F[B])(f: PartialFunction[Throwable, A]): F[Either[A, B]]
def catchNonFatalEither[A, AA >: A, B](fab: => F[Either[A, B]])(
f: PartialFunction[Throwable, AA]
): F[Either[AA, B]]
}
In practice, you don't need to use it directly because Fx
is already CanCatch
as well.
CanCatch.catchNonFatal
- Fx
- CanCatch
val fa: F[A] = ...
Fx[F].catchNonFatal(fa) {
case SomeException(message) =>
SomeError(message)
} // F[Either[SomeError, A]
val fa: F[A] = ...
CanCatch[F].catchNonFatal(fa) {
case SomeException(message) =>
SomeError(message)
} // F[Either[SomeError, A]
CanCatch[F].catchNonFatal[A, B]
lets you catch NonFatal
Throwable
from F[B]
and returns F[Either[A, B]]
.
How to Use
- IO
- Future
- Id
import cats.effect._
import effectie.core._
import effectie.instances.ce2.fx._
final case class MyException(cause: Throwable) extends RuntimeException
val fa = CanCatch[IO].catchNonFatal(
IO(throw new RuntimeException("Something's wrong!"))
) {
case ex =>
MyException(ex)
}
// fa: IO[Either[MyException, Nothing]] = Bind(
// source = Bind(
// source = Delay(
// ...
fa.unsafeRunSync()
// res1: Either[MyException, Nothing] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import effectie.core._
import effectie.instances.future.fx._
implicit val executorService: ExecutorService = Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
// executorService: ExecutorService = java.util.concurrent.ForkJoinPool@60cfb697[Running, parallelism = 4, size = 2, active = 0, running = 0, steals = 2, tasks = 0, submissions = 0]
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
// ec: ExecutionContext = scala.concurrent.impl.ExecutionContextImpl$$anon$4@3939259d
final case class MyException(cause: Throwable) extends RuntimeException
val fa = CanCatch[Future].catchNonFatal(
Future(throw new RuntimeException("Something's wrong!"))
) {
case ex =>
MyException(ex)
}
// fa: Future[Either[MyException, Nothing]] = Future(Success(Left(repl.MdocSession$App2$MyException)))
// Just for this example, you wouldn't want to do it in your production code
Await.result(fa, Duration.Inf)
// res3: Either[MyException, Nothing] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import effectie.core._
import effectie.instances.id.fx._
final case class MyException(cause: Throwable) extends RuntimeException
CanCatch[Id].catchNonFatal(
throw new RuntimeException("Something's wrong!")
) {
case ex =>
MyException(ex)
}
// res5: Id[Either[MyException, Nothing]] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Happy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
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
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
def main(arg: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
import effectie.instances.future.fx._
try {
val fa = doSomething[Future](1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Right(202)
// Result is 202
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](1)
// fa: Id[Either[MyError, Int]] = Right(value = 202)
fa 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
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](-101)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
val result = fa.unsafeRunSync()
// result: Either[MyError, Int] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
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]
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
import effectie.instances.future.fx._
try {
val fa = doSomething[Future](-101)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Left(NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1]))
// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1])
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](-101)
// fa: Id[Either[MyError, Int]] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
fa 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
- IO
- Future
- Id
import cats.effect._
import effectie.core._
import effectie.instances.ce2.fx._
final case class MyException(cause: Throwable) extends RuntimeException
val fa = CanCatch[IO].catchNonFatalEither(
IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int])
) {
case ex => MyException(ex)
}
// fa: IO[Either[Throwable, Int]] = Bind(
// source = Bind(
// source = Delay(
// ...
fa.unsafeRunSync()
// res19: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import effectie.core._
implicit val executorService: ExecutorService = Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
// executorService: ExecutorService = java.util.concurrent.ForkJoinPool@324d8add[Running, parallelism = 4, size = 3, active = 0, running = 0, steals = 2, tasks = 0, submissions = 0]
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
// ec: ExecutionContext = scala.concurrent.impl.ExecutionContextImpl$$anon$4@422844c4
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.future.fx._
val fa = CanCatch[Future].catchNonFatalEither(
Future((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int])
) {
case ex => MyException(ex)
}
// fa: Future[Either[Throwable, Int]] = Future(Success(Left(repl.MdocSession$App20$MyException)))
// Just for this example, you wouldn't want to do it in your production code
Await.result(fa, Duration.Inf)
// res21: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import effectie.core._
import effectie.instances.id.fx._
final case class MyException(cause: Throwable) extends RuntimeException
CanCatch[Id].catchNonFatalEither(
(throw new RuntimeException("Something's wrong!")): Either[Throwable, Int]
){
case ex => MyException(ex)
}
// res23: Id[Either[Throwable, Int]] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Happy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
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
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
import effectie.instances.future.fx._
try {
val fa = doSomething[Future](1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Right(200)
// Result is 200
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](1)
// fa: Id[Either[MyError, Int]] = Right(value = 200)
fa match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
// Result is 200
Unhappy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](-1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
val result = fa.unsafeRunSync()
// result: Either[MyError, Int] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
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])
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
import effectie.instances.future.fx._
try {
val fa = doSomething[Future](-1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Left(NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]))
// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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
) {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](-1)
// fa: Id[Either[MyError, Int]] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
fa 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
- IO
- Future
- Id
import cats.data.EitherT
import cats.effect._
import effectie.core._
import effectie.syntax.all._
import effectie.instances.ce2.fx._
final case class MyException(cause: Throwable) extends RuntimeException
val fa = CanCatch[IO].catchNonFatalEitherT(
EitherT(IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int]))
) {
case ex => MyException(ex)
}
// fa: EitherT[[α$0$]IO[α$0$], Throwable, Int] = EitherT(
// value = Bind(
// source = Bind(
// ...
fa.value.unsafeRunSync()
// res37: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats.data.EitherT
import effectie.core._
implicit val executorService: ExecutorService = Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
// executorService: ExecutorService = java.util.concurrent.ForkJoinPool@27f76fe6[Running, parallelism = 4, size = 3, active = 0, running = 0, steals = 2, tasks = 0, submissions = 0]
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
// ec: ExecutionContext = scala.concurrent.impl.ExecutionContextImpl$$anon$4@57f967db
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.syntax.all._
import effectie.instances.future.fx._
val fa = CanCatch[Future].catchNonFatalEitherT(
EitherT(Future((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int]))
) {
case ex => MyException(ex)
}
// fa: EitherT[[α$0$]Future[α$0$], Throwable, Int] = EitherT(
// value = Future(Success(Left(repl.MdocSession$App38$MyException)))
// )
// Just for this example, you wouldn't want to do it in your production code
Await.result(fa.value, Duration.Inf)
// res39: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.data.EitherT
import effectie.core._
import effectie.syntax.all._
import effectie.instances.id.fx._
final case class MyException(cause: Throwable) extends RuntimeException
val fa = CanCatch[Id].catchNonFatalEitherT(
EitherT((throw new RuntimeException("Something's wrong!")): Id[Either[Throwable, Int]])
) {
case ex => MyException(ex)
}
// fa: EitherT[[α$0$]Id[α$0$], Throwable, Int] = EitherT(
// value = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// ...
fa.value
// res41: Id[Either[Throwable, Int]] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Happy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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 <- pureOf(divide100By(n)).eitherT
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c
) {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.ce2.fx._
val fa = doSomething[IO](1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
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
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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 <- pureOf(divide100By(n)).eitherT
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c
) {
case ex => MyError.nonFatalThrowable(ex)
}.value
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
import effectie.instances.future.fx._
try {
val fa = doSomething[Future](1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Right(200)
// Result is 200
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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 <- pureOf(divide100By(n)).eitherT
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c
) {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.id.fx._
val fa = doSomething[Id](1)
// fa: Id[Either[MyError, Int]] = Right(value = 200)
fa match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
// Result is 200
Unhappy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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 <- pureOf(divide100By(n)).eitherT
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c
) {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.ce2.fx._
val fa = doSomething[IO](-1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
val result = fa.unsafeRunSync()
// result: Either[MyError, Int] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
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])
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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 <- pureOf(divide100By(n)).eitherT
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c
) {
case ex => MyError.nonFatalThrowable(ex)
}.value
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
import effectie.instances.future.fx._
try {
val fa = doSomething[Future](-1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Left(NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]))
// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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 <- pureOf(divide100By(n)).eitherT
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c
) {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.id.fx._
val fa = doSomething[Id](-1)
// fa: Id[Either[MyError, Int]] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
fa 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
- IO
- Future
- Id
import cats.effect._
import effectie.syntax.all._
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.ce2.fx._
val fa =
IO(throw new RuntimeException("Something's wrong!"))
.catchNonFatal {
case ex => MyException(ex)
}
// fa: IO[Either[MyException, Nothing]] = Bind(
// source = Bind(
// source = Delay(
// ...
fa.unsafeRunSync()
// res55: Either[MyException, Nothing] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import effectie.syntax.all._
implicit val executorService: ExecutorService = Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
// executorService: ExecutorService = java.util.concurrent.ForkJoinPool@15a6b993[Running, parallelism = 4, size = 3, active = 0, running = 0, steals = 2, tasks = 0, submissions = 0]
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
// ec: ExecutionContext = scala.concurrent.impl.ExecutionContextImpl$$anon$4@9fdd343
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.future.fx._
val fa =
Future(throw new RuntimeException("Something's wrong!"))
.catchNonFatal {
case ex => MyException(ex)
}
// fa: Future[Either[MyException, Nothing]] = Future(Success(Left(repl.MdocSession$App56$MyException)))
// Just for this example, you wouldn't want to do it in your production code
Await.result(fa, Duration.Inf)
// res57: Either[MyException, Nothing] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
Happy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
a <- pureOf(n + 100)
b <- effectOf(doSomethingBad(a))
} yield b)
.catchNonFatal {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
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
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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]] =
(for {
a <- pureOf(n + 100)
b <- effectOf(doSomethingBad(a))
} yield b)
.catchNonFatal {
case ex => MyError.nonFatalThrowable(ex)
}
def main(arg: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
try {
import effectie.instances.future.fx._
val fa = doSomething[Future](1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Right(202)
// Result is 202
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
a <- pureOf(n + 100)
b <- effectOf(doSomethingBad(a))
} yield b)
.catchNonFatal {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](1)
// fa: Id[Either[MyError, Int]] = Right(value = 202)
fa 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
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
a <- pureOf(n + 100)
b <- effectOf(doSomethingBad(a))
} yield b)
.catchNonFatal {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](-101)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
val result = fa.unsafeRunSync()
// result: Either[MyError, Int] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
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]
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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]] =
(for {
a <- pureOf(n + 100)
b <- effectOf(doSomethingBad(a))
} yield b)
.catchNonFatal {
case ex => MyError.nonFatalThrowable(ex)
}
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
try {
import effectie.instances.future.fx._
val fa = doSomething[Future](-101)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Left(NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1]))
// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -1])
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
a <- pureOf(n + 100)
b <- effectOf(doSomethingBad(a))
} yield b)
.catchNonFatal {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](-101)
// fa: Id[Either[MyError, Int]] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
fa 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
- IO
- Future
- Id
import cats.effect._
import effectie.syntax.all._
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.ce2.fx._
val fa =
IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int])
.catchNonFatalEither {
case ex => MyException(ex)
}
// fa: IO[Either[Throwable, Int]] = Bind(
// source = Bind(
// source = Delay(
// ...
fa.unsafeRunSync()
// res72: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import effectie.syntax.all._
implicit val executorService: ExecutorService = Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
// executorService: ExecutorService = java.util.concurrent.ForkJoinPool@5a969b40[Running, parallelism = 4, size = 3, active = 0, running = 0, steals = 2, tasks = 0, submissions = 0]
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
// ec: ExecutionContext = scala.concurrent.impl.ExecutionContextImpl$$anon$4@3d163c9a
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.future.fx._
val fa =
Future((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int])
.catchNonFatalEither {
case ex => MyException(ex)
}
// fa: Future[Either[Throwable, Int]] = Future(Success(Left(repl.MdocSession$App73$MyException)))
// Just for this example, you wouldn't want to do it in your production code
Await.result(fa, Duration.Inf)
// res74: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
Happy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
aOrB <- pureOf(divide100By(n))
c <- effectOf(aOrB.map(b => doSomethingBad(b)))
} yield c)
.catchNonFatalEither {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
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
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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]] =
(for {
aOrB <- pureOf(divide100By(n))
c <- effectOf(aOrB.map(b => doSomethingBad(b)))
} yield c)
.catchNonFatalEither {
case ex => MyError.nonFatalThrowable(ex)
}
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
try {
import effectie.instances.future.fx._
val fa = doSomething[Future](1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Right(200)
// Result is 200
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
aOrB <- pureOf(divide100By(n))
c <- effectOf(aOrB.map(b => doSomethingBad(b)))
} yield c)
.catchNonFatalEither {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](1)
// fa: Id[Either[MyError, Int]] = Right(value = 200)
fa match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
// Result is 200
Unhappy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
aOrB <- pureOf(divide100By(n))
c <- effectOf(aOrB.map(b => doSomethingBad(b)))
} yield c)
.catchNonFatalEither {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.ce2.fx._
val fa = doSomething[IO](-1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
val result = fa.unsafeRunSync()
// result: Either[MyError, Int] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
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])
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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]] =
(for {
aOrB <- pureOf(divide100By(n))
c <- effectOf(aOrB.map(b => doSomethingBad(b)))
} yield c)
.catchNonFatalEither {
case ex => MyError.nonFatalThrowable(ex)
}
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
try {
import effectie.instances.future.fx._
val fa = doSomething[Future](-1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Left(NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]))
// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
sealed trait MyError
object 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]] =
(for {
aOrB <- pureOf(divide100By(n))
c <- effectOf(aOrB.map(b => doSomethingBad(b)))
} yield c)
.catchNonFatalEither {
case ex => MyError.nonFatalThrowable(ex)
}
import effectie.instances.id.fx._
val fa = doSomething[Id](-1)
// fa: Id[Either[MyError, Int]] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
fa 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
- IO
- Future
- Id
import cats.data.EitherT
import cats.effect._
import effectie.syntax.all._
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.ce2.fx._
val fa =
EitherT(IO((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int]))
.catchNonFatalEitherT {
case ex => MyException(ex)
}
// fa: EitherT[[A]IO[A], Throwable, Int] = EitherT(
// value = Bind(
// source = Bind(
// ...
fa.value.unsafeRunSync()
// res89: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats.data.EitherT
import effectie.syntax.all._
implicit val executorService: ExecutorService = Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
// executorService: ExecutorService = java.util.concurrent.ForkJoinPool@392fc8cd[Running, parallelism = 4, size = 3, active = 0, running = 0, steals = 2, tasks = 0, submissions = 0]
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
// ec: ExecutionContext = scala.concurrent.impl.ExecutionContextImpl$$anon$4@2512f956
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.future.fx._
val fa =
EitherT(Future((throw new RuntimeException("Something's wrong!")): Either[Throwable, Int]))
.catchNonFatalEitherT {
case ex => MyException(ex)
}
// fa: EitherT[[T]Future[T], Throwable, Int] = EitherT(
// value = Future(Success(Left(repl.MdocSession$App90$MyException)))
// )
// Just for this example, you wouldn't want to do it in your production code
Await.result(fa.value, Duration.Inf)
// res91: Either[Throwable, Int] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.data.EitherT
import effectie.syntax.all._
final case class MyException(cause: Throwable) extends RuntimeException
import effectie.instances.id.fx._
val fa =
EitherT((throw new RuntimeException("Something's wrong!")): Id[Either[Throwable, Int]])
.catchNonFatalEitherT {
case ex => MyException(ex)
}
// fa: EitherT[[A]Id[A], Throwable, Int] = EitherT(
// value = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// ...
fa.value
// res93: Id[Either[Throwable, Int]] = Left(
// value = MyException(cause = java.lang.RuntimeException: Something's wrong!)
// )
Happy Path Example
- IO
- Future
- Id
import cats._
import cats.syntax.all._
import cats.data.EitherT
import cats.effect._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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]] =
(for {
b <- EitherT(pureOf(divide100By(n)))
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c)
.catchNonFatalEitherT {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.ce2.fx._
val fa = doSomething[IO](1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
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
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.data.EitherT
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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]] =
(for {
b <- EitherT(pureOf(divide100By(n)))
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c)
.catchNonFatalEitherT {
case ex => MyError.nonFatalThrowable(ex)
}.value
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
try {
import effectie.instances.future.fx._
val fa = doSomething[Future](1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Right(200)
// Result is 200
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.data.EitherT
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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]] =
(for {
b <- EitherT(pureOf(divide100By(n)))
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c)
.catchNonFatalEitherT {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.id.fx._
val fa = doSomething[Id](1)
// fa: Id[Either[MyError, Int]] = Right(value = 200)
fa match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
// Result is 200
Unhappy Path Example
- IO
- Future
- Id
import cats._
import cats.data.EitherT
import cats.syntax.all._
import cats.effect._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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]] =
(for {
b <- EitherT(pureOf(divide100By(n)))
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c)
.catchNonFatalEitherT {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.ce2.fx._
val fa = doSomething[IO](-1)
// fa: IO[Either[MyError, Int]] = Bind(
// source = Bind(
// source = Bind(
// ...
val result = fa.unsafeRunSync()
// result: Either[MyError, Int] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
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])
import java.util.concurrent.{ExecutorService, Executors}
import scala.concurrent.{ExecutionContext, Future, Await}
import scala.concurrent.duration._
import cats._
import cats.data.EitherT
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
import extras.concurrent.ExecutorServiceOps
object MyApp {
sealed trait MyError
object 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]] =
(for {
b <- EitherT(pureOf(divide100By(n)))
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c)
.catchNonFatalEitherT {
case ex => MyError.nonFatalThrowable(ex)
}.value
def main(args: Array[String]): Unit = {
val executorService: ExecutorService =
Executors.newWorkStealingPool(Runtime.getRuntime.availableProcessors())
implicit val ec: ExecutionContext = ExecutionContext.fromExecutorService(executorService)
try {
import effectie.instances.future.fx._
val fa = doSomething[Future](-1)
println(fa)
val result = Await.result(fa, 1.second)
println(result)
result match {
case Right(b) =>
println(s"Result is $b")
case Left(a) =>
println(s"Result: Failed with $a")
}
} finally {
ExecutorServiceOps.shutdownAndAwaitTermination(executorService, 1.second)
}
}
}
MyApp.main(Array.empty)
// Future(<not completed>)
// Left(NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100]))
// Result: Failed with NonFatalThrowable(java.lang.IllegalArgumentException: n cannot be a negative number. [n: -100])
Use of Id
is not recommended as Id
means having no Effect
. Use it only for some special cases like testing.
import cats._
import cats.data.EitherT
import cats.syntax.all._
import effectie.core._
import effectie.syntax.all._
import extras.cats.syntax.all._
sealed trait MyError
object 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]] =
(for {
b <- EitherT(pureOf(divide100By(n)))
c <- doSomethingBad(b).rightTF[F, MyError]
} yield c)
.catchNonFatalEitherT {
case ex => MyError.nonFatalThrowable(ex)
}.value
import effectie.instances.id.fx._
val fa = doSomething[Id](-1)
// fa: Id[Either[MyError, Int]] = Left(
// value = NonFatalThrowable(
// throwable = java.lang.IllegalArgumentException: n cannot be a negative numbe...
fa 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])