-
-
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
Implement EitherT#leftFlatMap and EitherT#leftSemiflatMap #1790
Implement EitherT#leftFlatMap and EitherT#leftSemiflatMap #1790
Conversation
Codecov Report
@@ Coverage Diff @@
## master #1790 +/- ##
==========================================
+ Coverage 94.96% 95.55% +0.58%
==========================================
Files 241 248 +7
Lines 4193 4451 +258
Branches 115 118 +3
==========================================
+ Hits 3982 4253 +271
+ Misses 211 198 -13
Continue to review full report at Codecov.
|
thanks very much! 👍 |
}) | ||
|
||
def leftSemiflatMap[D](f: A => F[D])(implicit F: Monad[F]): EitherT[F, D, B] = | ||
leftFlatMap(a => EitherT.left(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.
this is going to allocate the EitherT
just to call .value
above. Why not just inline the implementation:
EitherT(F.flatMap(value) {
case Left(a) => f(a).map { d => Left(d) }
case r@Right(_) => F.pure(r.leftCast)
})
8de4070
to
cd38406
Compare
@johnynek Back from holidays. Did just that. |
Seems like there should be a |
You mean |
Oh, yes, that's correct. |
I'd like to have tests for these in terms of et.leftFlatMap(a => f(a)) <-> et.swap.flatMap(a => f(a).swap) Does that seem reasonable? |
@non I tried to do it at first, but I missed one swap. It seems to work with one extra: et.leftFlatMap(a => f(a)) <-> et.swap.flatMap(a => f(a).swap).swap
|
@vendethiel Let's add these sorts of tests in addition to what you already have. I'd go ahead and write tests like this for all the left-biased methods that have a right-biased equivalent. |
That means 2 Okay, what about |
def leftFlatMap[BB >: B, D](f: A => EitherT[F, D, BB])(implicit F: Monad[F]): EitherT[F, D, BB] = | ||
EitherT(F.flatMap(value) { | ||
case Left(a) => f(a).value | ||
case r@Right(_) => F.pure(r.leftCast) |
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.
Tiny nitpick, but can we change this to case r: Right => ...
since we don't actually want to extract anything?
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 in practice I don't think it matters, the use of :
implies type casing which is generally used in contexts where parametricity is violated: https://typelevel.org/blog/2014/11/10/why_is_adt_pattern_matching_allowed.html
I could have sworn there were some cases where :
was allowed and ADT patmat wasn't but I can't remember.
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.
Very interesting article, thanks a lot! :)
I always thought that scalac transforms case t: Type
into an isInstanceOf
check whereas case Type(_)
would be turned into Type.unapply(...)
, but that article shows that my assumption was false.
It seems then, there's no real benefit to my suggestion, so disregard.
def leftSemiflatMap[D](f: A => F[D])(implicit F: Monad[F]): EitherT[F, D, B] = | ||
EitherT(F.flatMap(value) { | ||
case Left(a) => F.map(f(a)) { d => Left(d) } | ||
case r@Right(_) => F.pure(r.leftCast) |
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.
Ditto, as above :)
@vendethiel if you waiting for an answer for |
Okay, thanks. Then I think the PR is ready? |
I still think the tests @non suggested would be valuable to add. |
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.
Tiny note, just about naming of tests, otherwise LGTM :)
@@ -434,4 +434,22 @@ class EitherTTests extends CatsSuite { | |||
} yield s1 ++ s2 | |||
} | |||
|
|||
test("leftFlatMap") { | |||
forAll { (eithert: EitherT[List, String, Int], f: String => String) => |
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.
Could we name this one "leftFlatMap consistent with leftMap"?
forAll { (eithert: EitherT[List, String, Int], f: String => String) => | ||
eithert.leftFlatMap(v => EitherT.left[Int](List(f(v)))) should ===(eithert.leftMap(f)) | ||
} | ||
forAll { (eithert: EitherT[List, String, Int], f: String => EitherT[List, String, Int]) => |
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.
How about naming this one "leftFlatMap consistent with swap and then flatMap"?
} | ||
} | ||
|
||
test("leftSemiflatMap") { |
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.
Same as above for these two tests :)
Tiny nit
Somehow missed the notifications! Sorry. |
Don't be, it's all good! Thanks very much for your contribution! 😊 |
👍 Thanks very much indeed! |
Thanks! 🍰 |
Modeled after the right versions.