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

Cannot widen a HKT parameter with covariant type parameters #11789

Open
neko-kai opened this issue Oct 31, 2019 · 4 comments

Comments

@neko-kai
Copy link

@neko-kai neko-kai commented Oct 31, 2019

Example:

case class Holder[F[+_, +_]](v: F[Int, Int]) {
  def asWiden[F2[+x, +y] >: F[x, y]]: F2[Int, Int] = v
}

object example extends App {
  println(Holder[Right](Right(5)).asWiden[Either])
}

Produces error:

Error:(4, 16) covariant type x occurs in contravariant position in type [+x, +y] >: F[x,y] of type F2
  def asWiden[F2[+x, +y] >: F[x, y]]: F2[Int, Int] = v
              ^

Expected it to succeed, as the expression does not appear unsound!
Writing it as F2[+x, +y] >: F[_, _] passes the variance checker, but the expression using the function fails to typecheck:

Error:(7, 99) type arguments [Either] do not conform to method asWiden's type parameter bounds [F2[+x, +y] >: Right[_, _]]
def res0 = Holder[Right](Right(5)).asWiden[Either]                                                              ^

Scala version 2.13.1

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Nov 2, 2019

As all things variance it works when you flip it 😄

class Holder[F[-_]] {
  type G[+x] >: F[x]
}

How could F[-x] ever be a subtype of G[+x]?

Btw minimization:

class Holder[F[+_]] {
  type G[+x] >: F[x]
}
@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Nov 2, 2019

I guess the difference with dotty is the direct encoding of higher kinds / type lambdas.
In Scala we have PolyTypes with TypeBounds as the result type.
And in general we flip the variance for lower bounds.

This feels hacky but solves this one example.
Not sure if sound though:

--- a/src/reflect/scala/reflect/internal/Variances.scala
+++ b/src/reflect/scala/reflect/internal/Variances.scala
@@ -125,6 +125,7 @@ trait Variances {
       def apply(tp: Type): Type = {
         tp match {
           case _ if isUncheckedVariance(tp)                    =>
+          case PolyType(_, TypeBounds(lo, hi))                 => this(lo); this(hi)
           case _ if resultTypeOnly(tp)                         => this(tp.resultType)
           case TypeRef(_, sym, _) if shouldDealias(sym)        => this(tp.normalize)
           case TypeRef(_, sym, _) if !sym.variance.isInvariant => checkVarianceOfSymbol(sym) ; tp.mapOver(this)
@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Nov 2, 2019

So in general it's correct to not flip the variance when checking the poly type parameters but the problem is that there are many things (side effects) happening at once and I don't know how to special case it.

@joroKr21

This comment has been minimized.

Copy link

@joroKr21 joroKr21 commented Nov 10, 2019

More weirdness (I don't even know what is supposed to be correct):

scala> trait Foo[+A] { def foo: { type T <: A } }
trait Foo

scala> trait Foo[+A] { def foo: { type T >: A } }
                                       ^
       error: covariant type A occurs in contravariant position in type  >: A of type T
                           ^
       error: covariant type A occurs in contravariant position in type => AnyRef{type T >: A} of method foo

scala> trait Foo[+A] { def foo(x: { type T <: A }): Unit }
                               ^
       error: covariant type A occurs in contravariant position in type AnyRef{type T <: A} of value x

scala> trait Foo[+A] { def foo(x: { type T >: A }): Unit }
                                         ^
       error: covariant type A occurs in contravariant position in type  >: A of type T

scala> trait Foo[+A, x <: { type T <: A }]
trait Foo

scala> trait Foo[+A, x <: { type T >: A }]
                     ^
       error: covariant type A occurs in contravariant position in type  <: AnyRef{type T >: A} of type x

scala> trait Foo[+A, x <: { type T[_ <: A] }]
trait Foo

scala> trait Foo[+A, x <: { type T[_ >: A] }]
trait Foo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.