-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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 foldF, cataF and emptyflatTap to OptionT #3335
Conversation
Codecov Report
@@ Coverage Diff @@
## master #3335 +/- ##
=======================================
Coverage 92.70% 92.71%
=======================================
Files 379 379
Lines 7981 7985 +4
Branches 230 230
=======================================
+ Hits 7399 7403 +4
Misses 582 582
Continue to review full report at Codecov.
|
7b1bfed
to
3dfd6bb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me. I'm happy to resolve the merge conflict (it's just imports following a testing change) if you'd like.
# Conflicts: # tests/src/test/scala/cats/tests/OptionTSuite.scala
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 on green.
@travisbrown thank you (also for the offer to resolve the conflict)! Just fixed the formatting, all checks green now. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thanks, now we just need a second review!
* Perform an effect if the value inside the is a `None`, leaving the value untouched. Equivalent to [[orElseF]] | ||
* with an effect returning `None` as argument. | ||
*/ | ||
def flatTapNone[B](f: => F[B])(implicit F: Monad[F]): OptionT[F, A] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def flatTapNone[B](f: => F[B])(implicit F: Monad[F]): OptionT[F, A] = | |
def flatTapNone[B](ifNone: => F[B])(implicit F: Monad[F]): OptionT[F, A] = |
* res0: List[Int] = List(23, 46) | ||
* }}} | ||
*/ | ||
def foldF[B](default: => F[B])(f: A => F[B])(implicit F: FlatMap[F]): F[B] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def foldF[B](default: => F[B])(f: A => F[B])(implicit F: FlatMap[F]): F[B] = | |
def foldF[B](ifNone: => F[B])(withSome: A => F[B])(implicit F: FlatMap[F]): F[B] = |
Should we add more informative names?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I agree with your intention (and have hence changed it on flatTapNone
– thx), I would value consistency with fold
more and keep the parameter names the same.
* one parameter list, which can result in better type inference in some | ||
* contexts. | ||
*/ | ||
def cataF[B](default: => F[B], f: A => F[B])(implicit F: FlatMap[F]): F[B] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am aware of the notions of catamorphisms, but is that also a unified term or notion across cats
? I can only grep the words def cata
in OptionT
and in CoFree
.
Unless we want to extend the use across the whole of cats
, perhaps we should drop them, to avoid confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a cata
to fold
already, so when seeing foldF
, I'd expect cataF
there as well. Whether cata
should have been introduced with that name in the first place seems to be outside of the scope of this PR…but that's just my two cents.
@@ -45,6 +68,9 @@ final case class OptionT[F[_], A](value: F[Option[A]]) { | |||
def semiflatMap[B](f: A => F[B])(implicit F: Monad[F]): OptionT[F, B] = | |||
flatMap(a => OptionT.liftF(f(a))) | |||
|
|||
def semiflatTap[B](f: A => F[B])(implicit F: Monad[F]): OptionT[F, A] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have had this conversation before: given that the B
has no relevance on the types of the outputs, should we just make it an existential?
def semiflatTap[B](f: A => F[B])(implicit F: Monad[F]): OptionT[F, A] = | |
def semiflatTap(withSome: A => F[_])(implicit F: Monad[F]): OptionT[F, A] = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FlatMap
has this type parameter for flatTap
, EitherT
has it for it's semiFlatTap
, so again, I would vote for consistency to give cats users a reliable experience in terms of how signatures look and when type inference works. Or do you see any other advantage of the existential which would outweigh this aspect?
…tiont # Conflicts: # tests/src/test/scala/cats/tests/OptionTSuite.scala
@travisbrown As you approved the code once already and I changed only an argument name since then, would you mind merging? 🙏 |
Sure, I’ll merge when I’m back at a computer. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #3335 +/- ##
=======================================
Coverage 92.70% 92.71%
=======================================
Files 379 379
Lines 7981 7985 +4
Branches 230 230
=======================================
+ Hits 7399 7403 +4
Misses 582 582 |
Adds several handy methods to
OptionT
:foldF
allows to fold effectually, similar toEitherT.foldF
cataF
is a variant offoldF
with one parameter list, similar ascata
is tofold
emptyflatTap
(suggestions for better names welcome) allows to perform an effect if theOptionT
carries aNone
value inside, leaving the result unchanged (except if raising an error, that would propagate).An example for a use case for
emptyflatTap
is looking up a value in a cache (returningF[Option[V]]
which you wrap inOptionT
for further handling, but logging (asF[Unit]
) if the value was empty: