-
-
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 Foldable
, Traverse
and Comonad
instances to WriterT
#2182
Conversation
Codecov Report
@@ Coverage Diff @@
## master #2182 +/- ##
==========================================
- Coverage 94.75% 94.73% -0.03%
==========================================
Files 330 330
Lines 5568 5582 +14
Branches 201 204 +3
==========================================
+ Hits 5276 5288 +12
- Misses 292 294 +2
Continue to review full report at Codecov.
|
I’m surprised this passes binary compatibility, but assuming that’s correct I’m +1 |
@@ -63,6 +63,17 @@ final case class WriterT[F[_], L, V](run: F[(L, V)]) { | |||
mapWritten(_ => monoidL.empty) | |||
|
|||
def show(implicit F: Show[F[(L, V)]]): String = F.show(run) | |||
|
|||
def foldLeft[C](c: C)(f: (C, V) => C)(implicit F: Foldable[F]): C = | |||
F.foldLeft(run, c)((z, v) => f(z, v._2)) |
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.
Can we call v
instead lv
here to remind us it has the written value?
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.
Sure thing, thanks
F.foldLeft(run, c)((z, v) => f(z, v._2)) | ||
|
||
def foldRight[C](lc: Eval[C])(f: (V, Eval[C]) => Eval[C])(implicit F: Foldable[F]): Eval[C] = | ||
F.foldRight(run, lc)((v, z) => f(v._2, z)) |
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.
Can we call v as lv here too?
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.
done
@johnynek not sure why it passes, I'm refreshing right now the rules of binary compatibility. What's the change here that you are not 100% sure about ? Because I might work it around even if it passes. Thanks for the help. |
@@ -95,9 +106,21 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { | |||
implicit val F0: Monad[F] = F | |||
implicit val L0: Monoid[L] = L | |||
} | |||
|
|||
implicit def catsDataFoldableForWriterT[F[_], L, V](implicit F: Foldable[F]): Foldable[WriterT[F, L, ?]] = |
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.
The Traverse
instance is more specific and thus should have a higher priority than the Foldable
instance.
In case you are curious, here is the guideline in this regard.
https://typelevel.org/cats/guidelines.html#a-idimplicit-priority-hrefimplicit-prioritya-implicit-instance-priority
I guess you can leave the instance of Traverse
where you have now and move the Foldable
instance down to WriterTInstance1
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.
thanks, makes perfectly sense. I wasn't sure about the precedence there, I should have read the guidelines myself. I'm changing it.
I believe the renaming of package private classes is binary compatible. If that's what @johnynek was referring to. |
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.
thanks very much!
Thank you lads for helping through the process. 👍 |
Considering that this is still open I would add the |
Sounds good :) |
Foldable
, Traverse
and Comonad
instances to WriterT
@barambani , I took the liberty to update the PR title, if you don't mind. |
Thanks for doing it. I will also make the description more accurate |
Can you add resolution tests for |
And thanks a lot by the way. This is great! |
You're right, that's not resolved. I'm adding the tests and the fix. Thanks a lot |
Just for my understanding, could you point me to some details about why scalac fails to summon |
@@ -95,9 +106,23 @@ private[data] sealed abstract class WriterTInstances extends WriterTInstances0 { | |||
implicit val F0: Monad[F] = F | |||
implicit val L0: Monoid[L] = L | |||
} | |||
|
|||
implicit def catsDataTraverseForWriterTId[L](implicit F: Traverse[Id]): Traverse[WriterT[Id, L, ?]] = |
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.
@barambani you can't have any instance that potentially conflicts in the same trait level.
I think there is Functor
instance conflict here for [WriterT[Id, L, ?]
. I think you might need to create quite a few more priority layers to separate the instances.
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.
to be clear I didn't see the actually error message on travis. But I think separating them out might solve the problem.
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.
Sorry for the confusion, my bad for the lack of clarity. This prioritisation should work (let's see when the CI completes). My question was more in general about the reason why we needed to create instances for Id
. I would have expected that something like Comonad[WriterT[Id, ListWrapper[Int], ?]]
could summon the instance for F[_]
and I was wondering if you had resources about why that's not the case.
This doesn't mean that I'm not happy to add more priority levels if you think that's better. Thanks for the great help here.
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.
@barambani I don't know the underlying reason that Id
needs to be special-cased, but it's a weakness in scalac's type inference. This is a related Stack Overflow post that I've seen before. Something like Id
might be a tricky one because A =:= Id[A] =:= Id[Id[A]]
, so there may be some logic to short-circuit inference when applying a type results in the same type so you don't end up in an infinite loop or something. This is pure speculation :)
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.
@ceedubs thanks a lot, I didn't want for you to waste time on this. I was just taking advantage of this discussion in case you knew it out of your head 😃 👍
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.
👍 LGTM thanks a lot, @barambani!
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.
Thanks @barambani, I left two small comments.
def foldRight[A, B](fa: WriterT[F, L, A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = fa.foldRight(lb)(f) | ||
} | ||
|
||
private[data] sealed trait WriterTTraverse[F[_], L] extends Traverse[WriterT[F, L, ?]] with WriterTFoldable[F, L] { |
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.
If you extends WriterTFunctor
here as well, you get its map
implementation instead of the default traverse[Id]
from Traverse
.
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 will change it thanks 👍
|
||
def traverse[G[_], V1](f: V => G[V1])(implicit F: Traverse[F], G: Applicative[G]): G[WriterT[F, L, V1]] = | ||
G.map( | ||
F.traverse(run)(lv => G.product(G.pure(lv._1), f(lv._2))) |
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.
Can this be G.tupleLeft(f(lv._2), lv._1)
instead of G.product(G.pure(lv._1), f(lv._2))
?
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.
sure changing it 👍
|
||
override implicit def F0: Traverse[F] | ||
|
||
override def map[A, B](fa: WriterT[F, L, A])(f: A => B): WriterT[F, L, B] = super[WriterTFunctor].map(fa)(f) |
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 think we can omit this line if we defined map
in WriterTFunctor
as override def map
.
See for example EitherTFunctor
and EitherTMonad
(although EitherTTraverse
doesn't extends EitherTFunctor
at the moment and neither does OptionTTraverse
OptionTFunctor
).
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 true, apologies. I'm fixing it. I rushed it in. I have to stop reading those intellij errors. Do you think I should change also EitherTTraverse
and OptionTTraverse
accordingly ?
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 think there are probably more cases than just EitherTTraverse
and OptionTTraverse
which can be improved, I would leave those for another PR.
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.
LGTM :)
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.
Thanks again @barambani (and for the quick follow up on my nitpicking).
This adds
Foldable
,Traverse
andComonad
instances for WriterT. The relevant issue is #620