-
-
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
Enable SI-2712 fix in cats / Remove unapply machinery #1583
Conversation
@@ -85,7 +85,7 @@ class RegressionTests extends CatsSuite { | |||
|
|||
test("#500: foldMap - traverse consistency") { | |||
assert( | |||
List(1,2,3).traverseU(i => Const(List(i))).getConst == List(1,2,3).foldMap(List(_)) | |||
List(1,2,3).traverse(i => Const.of[List[Int]](List(i))).getConst == List(1,2,3).foldMap(List(_)) |
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.
for some reason the traverse
version can no longer infer the type correctly. Thus I had to pass the type into Const
's apply method. For convenience, I added an of
factory method to Const
. Please shout out if you know a better solution.
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.
That's not nice. Perhaps Const
needs its type params reversed?
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.
would that affect partial unification?
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.
Partial unification is strictly left to right. If Const varies on the left (can't look right now), then it wouldn't work with partial unification.as a rule of thumb, think about how you would declare the type constructor in Haskell.
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.
@djspiewak it varies on the right. @edmundnoble any other suggestion in terms of what to do with this?
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.
Might it have something to do with Scala inferring Nothing
on the unused type parameter which can make the compiler freak out since it treats Nothing
specially?
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 that specifying the type parameter here is an acceptable tradeoff for getting rid of the Unapply
machinery, but paging @milessabin to see if he has any thoughts on this popping up.
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 chatted with @milessabin IRT to this.
Here is the minimised version in case anyone is interested.
def trav[F[_], A, B](f: A => F[B]): F[B] = ???
case class C[A, B](a: A)
val c: C[Int, Int] = trav(C(_:Int)) //compiles
val a: Int = trav(C(_:Int)).a //doesn’t compile
[error] found : Int => C[Int,Nothing]
[error] required: Int => C[Int,B]
[error] val a: Int = trav(C(_:Int)).a
Our conclusion is that might have to live with this as is.
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.
What happens if you make C
covariant in 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.
Nevermind, I just tried it myself. It resolves the issue.
Codecov Report
@@ Coverage Diff @@
## master #1583 +/- ##
==========================================
+ Coverage 93.08% 93.38% +0.29%
==========================================
Files 250 240 -10
Lines 3989 3943 -46
Branches 138 141 +3
==========================================
- Hits 3713 3682 -31
+ Misses 276 261 -15
Continue to review full report at Codecov.
|
@@ -39,6 +39,12 @@ final case class Const[A, B](getConst: A) { | |||
object Const extends ConstInstances { | |||
def empty[A, B](implicit A: Monoid[A]): Const[A, B] = | |||
Const(A.empty) | |||
|
|||
class partialApply[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.
Perhaps change this to something like OfPartiallyApplied
to be consistent with https://github.com/typelevel/cats/blob/master/core/src/main/scala/cats/data/EitherT.scala#L275
Thanks to @djspiewak, the type inference glitch is resolved. Does any maintainer have time to give it another look? |
I'm not sure about making There's some more information about phantom types and variance on the typelevel blog about phantom types in scala for anyone who is interested. |
cc @djspiewak about my last message. You had good insight that making the parameter covariant might get around the inference issue. What are your thoughts on actually doing this for |
@ceedubs is it a phantom type? I'm literally not sure. It appears in both covariant and contravariant positions (as part of type constructors such as I agree it that feels hackish when the only reason to chose this variance is for type inference. On the other hand, for |
@ceedubs It could certainly be either covariant or contravariant, and so by that argument would correctly be invariant. However, Scala's type inference biases downward, not upward and not "in place". It always tries to drive things to Though I agree it seems awfully arbitrary. @kailuowang As long as there's no need to subtype |
I share everyone's discomfort, but I think this is the right thing to do. |
Thanks for the input @djspiewak and @milessabin. @djspiewak you raise some good points, and I don't think that I'm opposed to the @kailuowang thank you I'm very excited about this change! I assume that kittens will have to be updated in a similar way (especially since I think that it's outside of the scope of this PR, but I wonder if it would be helpful to have a FAQ item for "Why doesn't the code example in the Cats docs compile for me?" which could have an answer that links to both the kind-projector and si-2712 items. |
@kailuowang sorry but it looks like there are merge conflicts :( |
Resolved the conflicts and squashed. |
@kailuowang I don't see any |
@ceedubs, sorry I should've been more specific, it was late when I wrote the last update. |
@kailuowang hmm, I wonder if there are many places where code outside of Cats is using the Cats Aside from the question of whether there should still be an |
@@ -106,7 +106,7 @@ nested.map(_ + 1) | |||
``` | |||
|
|||
The `Nested` approach, being a distinct type from its constituents, will resolve the usual way modulo | |||
possible [SI-2712][si2712] issues, but requires syntactic and runtime overhead from wrapping and | |||
possible [SI-F2712][si2712] issues, but requires syntactic and runtime overhead from wrapping and |
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.
Was this change intentional? SI-F2712
doesn't mean anything to me.
val a: Either[String, Int]= Right(42) | ||
val b: FreeT[Option, Either[String, ?], Int] = FreeT.liftTU(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.
Could we keep a test like this but make sure that it works with the normal FreeT.liftT
?
implicit val eq5 = EitherT.catsDataEqForEitherT[OptionT[SEither, ?], String, Int] | ||
implicit val eq6 = OptionT.catsDataEqForOptionT[SEither, (Int, Int, Int)] | ||
|
||
implicit val iso = CartesianTests.Isomorphisms.invariant[OptionT[SEither, ?]] |
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 love to see all of this go away! 😻
Oops I'm bad at the GitHub review system. I don't think I submitted #1583 (comment) until just now. |
So, the side effect of this change is that I imagine this will break some use cases. It is certainly the case that in principle a phantom type can be set to be covariant, but the meaning of the type is considerably changed, isn't it? |
@ceedubs @johnynek I reverted the change of making |
It's been a while and there have been some changes since @adelbertc's review, so I'm going to wait for another review or two before merging. |
@@ -6,6 +6,7 @@ import cats.functor.Contravariant | |||
/** | |||
* [[Const]] is a phantom type, it does not contain a value of its second type parameter `B` | |||
* [[Const]] can be seen as a type level version of `Function.const[A, B]: A => B => A` | |||
* B is set covariant to help type inference. |
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.
You meant invariant
?
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.
Oh, I saw a discussion below, I think you forgot to make B
covariant in Const
definition
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 reverted that in this PR and created a new Issue #1608 for it, but I forgot to remove this sentence. Thanks for spotting it!
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.
docs/src/main/tut/faq.md
Outdated
@@ -47,13 +49,22 @@ There are a few minor mismatches between `Xor` and `Either`. For example, in som | |||
|
|||
Similarly, `cats.data.XorT` has been replaced with `cats.data.EitherT`, although since this is a type defined in Cats, you don't need to import syntax or instances for it (although you may need imports for the underlying monad). | |||
|
|||
## <a id="si-2712" href="#si-2712"></a>Why is the compiler having trouble with types with more than one type parameters? |
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.
s/parameters/parameter
👍 LGTM. @johnynek would you want to take another look after the most recent changes? |
👍 |
fixes #1073
I also added an FAQ item to help people running into SI-2712