Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

attemptTap #3459

Merged
merged 5 commits into from
Jul 5, 2020
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions core/src/main/scala/cats/syntax/monadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure exactly what common pattern this is replacing? I guess exactly what the implementation is doing?

Copy link
Author

@RaasAhsan RaasAhsan Jun 9, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have an analogue for Future#onSuccess/Future#foreach (FlatMap#flatTap) and Future#onError (ApplicativeError#onError). This would be the pure analogue to Future#onComplete, which is useful for use cases like reporting metrics or logging. This function makes call chains a bit more compact.

fa
  .attempt
  .flatTap {
    case Right(a) => log.info(s"succeeded with $a")
    case Left(e) => log.error(s"failed with $e")
  }
  .rethrow

// Ironically this one has fewer LOC but harder to factor out the bodies
fa
  .flatTap(a => log.info(s"succeeded with $a"))
  .onError(e => log.error(s"failed with $e"))

fa.attemptTap {
  case Right(a) => log.info(s"succeeded with $a")
  case Left(e) => log.error(s"failed with $e")
}

aside: I could see an argument for accepting f as a PartialFunction

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see what you mean. Alrighty, I'm broadly 👍 on this then. Re your aside, I think the total function is better since Scala's PartialFunction/Function1 inheritance is backwards, so this is the more flexible arrangement.

}

final class MonadErrorRethrowOps[F[_], E, A](private val fea: F[Either[E, A]]) extends AnyVal {
Expand Down