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

Add semiflatTap and leftSemiflatTap functions to EitherT #3316

Merged
merged 2 commits into from
Feb 27, 2020

Conversation

matwojcik
Copy link
Contributor

semiflatTaps functions on EitherT are highly useful when you have some e.g. Logger[F] abstractions and you don't want to lift it to EitherT[F,A,B]

kubukoz
kubukoz previously approved these changes Feb 24, 2020
@codecov-io
Copy link

codecov-io commented Feb 24, 2020

Codecov Report

Merging #3316 into master will increase coverage by 0.22%.
The diff coverage is 100%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3316      +/-   ##
==========================================
+ Coverage    93.2%   93.42%   +0.22%     
==========================================
  Files         377      378       +1     
  Lines        7604     7631      +27     
  Branches      199      206       +7     
==========================================
+ Hits         7087     7129      +42     
+ Misses        517      502      -15
Flag Coverage Δ
#scala_version_212 93.49% <100%> (?)
#scala_version_213 93.17% <100%> (-0.03%) ⬇️
Impacted Files Coverage Δ
core/src/main/scala/cats/data/EitherT.scala 98.5% <100%> (+0.01%) ⬆️
core/src/main/scala/cats/Show.scala 100% <0%> (ø) ⬆️
core/src/main/scala/cats/instances/tuple.scala 100% <0%> (ø) ⬆️
core/src/main/scala/cats/data/Const.scala 100% <0%> (ø) ⬆️
core/src/main/scala/cats/data/Validated.scala 100% <0%> (ø) ⬆️
core/src/main/scala/cats/package.scala 100% <0%> (ø) ⬆️
...cala-2.12/cats/kernel/compat/TraversableOnce.scala 0% <0%> (ø)
core/src/main/scala/cats/data/NonEmptyList.scala 98.73% <0%> (+0.01%) ⬆️
core/src/main/scala/cats/data/NonEmptyVector.scala 99.25% <0%> (+0.01%) ⬆️
core/src/main/scala/cats/data/NonEmptySet.scala 97.64% <0%> (+0.02%) ⬆️
... and 11 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 1ede81e...74cd724. Read the comment docs.

Copy link
Contributor

@travisbrown travisbrown left a comment

Choose a reason for hiding this comment

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

I like this addition, thanks!

This is a case where I'd personally be inclined to move the implementation down a couple of levels:

  def semiflatTap[C](f: B => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
    EitherT(F.flatMap(value) {
      case l @ Left(_) => F.pure(EitherUtil.rightCast(l))
      case Right(b)    => F.as(f(b), Right(b))
    })

This isn't quite as clear as your version, but in my view the difference in readability is pretty small, and it avoids a lot of unnecessary wrapping and unwrapping. I don't think we need to worry too much about optimization here, but if we can avoid some allocations more or less for free I think we should.

(For what it's worth I also think we should do the same thing for the existing semiflatMap and leftSemiflatMap implementations.)

core/src/main/scala/cats/data/EitherT.scala Outdated Show resolved Hide resolved
@matwojcik
Copy link
Contributor Author

@travisbrown I'm not fully convinced ;) In the end it uses flatMap which resembles your suggestion however it does indeed one extra wrapping and unwrapping.

So it adds one wrap/unwrap for a price of more readable code and additionally existing code is reused. Plus it isn't a method like flatMap/map which would be used all over again in the same stack, so the penalty should be low.

However I'm not that attached to the original solution, so can change it if you insist ;)

@travisbrown
Copy link
Contributor

@matwojcik It's not a blocker for me. The signature is the important part, if someone wants to make an argument that we should change the implementations later they can.

@travisbrown
Copy link
Contributor

The test failure was BigDecimal commutativity on 2.13, which is a known issue, so I'm restarting that job.

I have an only moderately controversial fix for that problem that's waiting for another sign-off: #3303

def semiflatTap[C](f: B => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
semiflatMap(b => F.as(f(b), b))

def leftSemiflatTap[C](f: A => F[C])(implicit F: Monad[F]): EitherT[F, A, B] =
Copy link
Member

Choose a reason for hiding this comment

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

What do we think about using Unit here to signify that this resulting type is going to be discarded? Might be a bit more cumbersome on the usage side, but it's just an extra .void

Copy link
Contributor

@travisbrown travisbrown Feb 24, 2020

Choose a reason for hiding this comment

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

We already have a flatTap on FlatMap that doesn't require Unit, and tap, tapWith, etc. on Kleisli don't constrain the output type to be Unit. My personal preference would be consistency—I think that plus slightly easier use outweighs a little extra expressivity in the types.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@LukaJCB I first thought about it, but then saw that FlatMap[F].flatTap takes A => F[B] instead of A => F[Unit], so I wanted to be consistent with existing signature.

@travisbrown travisbrown merged commit d383422 into typelevel:master Feb 27, 2020
scala-steward pushed a commit to scala-steward/cats that referenced this pull request Feb 27, 2020
)

* Add semiflatTap and leftSemiflatTap functions to EitherT

* Replaces Monad[F] with F
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants