From cc9bf65212df31b9d367562c8aba988c075d93d5 Mon Sep 17 00:00:00 2001 From: Raas Ahsan Date: Sun, 7 Jun 2020 13:29:40 -0500 Subject: [PATCH 1/4] attemptTap --- core/src/main/scala/cats/syntax/monadError.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/main/scala/cats/syntax/monadError.scala b/core/src/main/scala/cats/syntax/monadError.scala index f098d3c6cc..1a828e20e7 100644 --- a/core/src/main/scala/cats/syntax/monadError.scala +++ b/core/src/main/scala/cats/syntax/monadError.scala @@ -32,6 +32,9 @@ final class MonadErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal { def redeemWith[B](recover: E => F[B], bind: A => F[B])(implicit F: MonadError[F, E]): F[B] = F.redeemWith[A, B](fa)(recover, bind) + + def attemptTap[B](f: Either[E, A] => F[B])(implicit F: MonadError[F, E]): F[A] = + F.rethrow(F.flatTap(F.attempt(fa))(f)) } final class MonadErrorRethrowOps[F[_], E, A](private val fea: F[Either[E, A]]) extends AnyVal { From db5807312139cc76ec504506f94acce922cb62fa Mon Sep 17 00:00:00 2001 From: Raas Ahsan Date: Tue, 16 Jun 2020 17:54:40 -0500 Subject: [PATCH 2/4] add to typeclass --- core/src/main/scala/cats/MonadError.scala | 26 +++++++++++++++++++ .../main/scala/cats/syntax/monadError.scala | 3 --- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/cats/MonadError.scala b/core/src/main/scala/cats/MonadError.scala index 4a151c0b36..a3d4ba969a 100644 --- a/core/src/main/scala/cats/MonadError.scala +++ b/core/src/main/scala/cats/MonadError.scala @@ -77,6 +77,32 @@ trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] { def redeemWith[A, B](fa: F[A])(recover: E => F[B], bind: A => F[B]): F[B] = flatMap(attempt(fa))(_.fold(recover, bind)) + /** + * Reifies the value or error of the source and performs an action on the result, + * then recovers the original value or error back into `F`. + * + * Alias for `fa.attempt.flatTap(f).rethrow` for convenience. + * + * Example: + * {{{ + * scala> import cats.implicits._ + * scala> import scala.util.{Try, Success} + * + * scala> def logErrors(result: Either[Throwable, Int]): Try[Unit] = + * + * scala> val a: Try[Either[Throwable, Int]] = Success(Left(new java.lang.Exception)) + * scala> a.attemptTap(logErrors) + * res0: scala.util.Try[Int] = Failure(java.lang.Exception) + * + * scala> val b: Try[Either[Throwable, Int]] = Success(Right(1)) + * scala> b.attemptTap(logErrors) + * res1: scala.util.Try[Int] = Success(1) + * }}} + * + */ + def attemptTap[A, B](fa: F[A])(f: Either[E, A] => F[B]): F[A] = + rethrow(flatTap(attempt(fa))(f)) + override def adaptError[A](fa: F[A])(pf: PartialFunction[E, E]): F[A] = recoverWith(fa)(pf.andThen(raiseError[A] _)) } diff --git a/core/src/main/scala/cats/syntax/monadError.scala b/core/src/main/scala/cats/syntax/monadError.scala index 1a828e20e7..f098d3c6cc 100644 --- a/core/src/main/scala/cats/syntax/monadError.scala +++ b/core/src/main/scala/cats/syntax/monadError.scala @@ -32,9 +32,6 @@ final class MonadErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal { def redeemWith[B](recover: E => F[B], bind: A => F[B])(implicit F: MonadError[F, E]): F[B] = F.redeemWith[A, B](fa)(recover, bind) - - def attemptTap[B](f: Either[E, A] => F[B])(implicit F: MonadError[F, E]): F[A] = - F.rethrow(F.flatTap(F.attempt(fa))(f)) } final class MonadErrorRethrowOps[F[_], E, A](private val fea: F[Either[E, A]]) extends AnyVal { From 6b462dbfe99d6820deecd63348435fe2a2f6d33b Mon Sep 17 00:00:00 2001 From: Raas Ahsan Date: Tue, 16 Jun 2020 18:25:28 -0500 Subject: [PATCH 3/4] Add attemptTap example --- core/src/main/scala/cats/MonadError.scala | 16 +++++++++++----- core/src/main/scala/cats/syntax/monadError.scala | 3 +++ 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/src/main/scala/cats/MonadError.scala b/core/src/main/scala/cats/MonadError.scala index a3d4ba969a..fa62d5f9d3 100644 --- a/core/src/main/scala/cats/MonadError.scala +++ b/core/src/main/scala/cats/MonadError.scala @@ -88,17 +88,23 @@ trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] { * scala> import cats.implicits._ * scala> import scala.util.{Try, Success} * - * scala> def logErrors(result: Either[Throwable, Int]): Try[Unit] = - * - * scala> val a: Try[Either[Throwable, Int]] = Success(Left(new java.lang.Exception)) + * scala> def logErrors(result: Either[Throwable, Int]): Try[Unit] = Try { + * result match { + * case Right(value) => println(s"Success: $value") + * case Left(_) => println("Failed") + * } + * } + * + * scala> val a: Try[Int] = Success(new java.lang.Exception) * scala> a.attemptTap(logErrors) + * Failed * res0: scala.util.Try[Int] = Failure(java.lang.Exception) * - * scala> val b: Try[Either[Throwable, Int]] = Success(Right(1)) + * scala> val b: Try[Int] = Success(1) * scala> b.attemptTap(logErrors) + * Success: 1 * res1: scala.util.Try[Int] = Success(1) * }}} - * */ def attemptTap[A, B](fa: F[A])(f: Either[E, A] => F[B]): F[A] = rethrow(flatTap(attempt(fa))(f)) diff --git a/core/src/main/scala/cats/syntax/monadError.scala b/core/src/main/scala/cats/syntax/monadError.scala index f098d3c6cc..fa75dff02f 100644 --- a/core/src/main/scala/cats/syntax/monadError.scala +++ b/core/src/main/scala/cats/syntax/monadError.scala @@ -32,6 +32,9 @@ final class MonadErrorOps[F[_], E, A](private val fa: F[A]) extends AnyVal { def redeemWith[B](recover: E => F[B], bind: A => F[B])(implicit F: MonadError[F, E]): F[B] = F.redeemWith[A, B](fa)(recover, bind) + + def attemptTap[B](f: Either[E, A] => F[B])(implicit F: MonadError[F, E]): F[A] = + F.attemptTap(fa)(f) } final class MonadErrorRethrowOps[F[_], E, A](private val fea: F[Either[E, A]]) extends AnyVal { From f78dc9221e4a1bcc4f68e404ed8d69a76b66489d Mon Sep 17 00:00:00 2001 From: Raas Ahsan Date: Tue, 16 Jun 2020 19:22:20 -0500 Subject: [PATCH 4/4] Fix doc --- core/src/main/scala/cats/MonadError.scala | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/core/src/main/scala/cats/MonadError.scala b/core/src/main/scala/cats/MonadError.scala index fa62d5f9d3..97e8264250 100644 --- a/core/src/main/scala/cats/MonadError.scala +++ b/core/src/main/scala/cats/MonadError.scala @@ -78,31 +78,26 @@ trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] { flatMap(attempt(fa))(_.fold(recover, bind)) /** - * Reifies the value or error of the source and performs an action on the result, + * Reifies the value or error of the source and performs an effect on the result, * then recovers the original value or error back into `F`. * + * Note that if the effect returned by `f` fails, the resulting effect will fail too. + * * Alias for `fa.attempt.flatTap(f).rethrow` for convenience. * * Example: * {{{ * scala> import cats.implicits._ - * scala> import scala.util.{Try, Success} + * scala> import scala.util.{Try, Success, Failure} * - * scala> def logErrors(result: Either[Throwable, Int]): Try[Unit] = Try { - * result match { - * case Right(value) => println(s"Success: $value") - * case Left(_) => println("Failed") - * } - * } + * scala> def checkError(result: Either[Throwable, Int]): Try[String] = result.fold(_ => Failure(new java.lang.Exception), _ => Success("success")) * - * scala> val a: Try[Int] = Success(new java.lang.Exception) - * scala> a.attemptTap(logErrors) - * Failed + * scala> val a: Try[Int] = Failure(new Throwable("failed")) + * scala> a.attemptTap(checkError) * res0: scala.util.Try[Int] = Failure(java.lang.Exception) * * scala> val b: Try[Int] = Success(1) - * scala> b.attemptTap(logErrors) - * Success: 1 + * scala> b.attemptTap(checkError) * res1: scala.util.Try[Int] = Success(1) * }}} */