Skip to content

Worse type inference when writing (x: Foo[_ >: A]) as opposed to [A1 >: A](x: Foo[A1]) #10920

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

Open
smarter opened this issue Jun 4, 2018 · 10 comments
Labels
fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) infer library:collections minimized typer usability
Milestone

Comments

@smarter
Copy link
Member

smarter commented Jun 4, 2018

In the following code, bar1 and bar2 are equivalent:

trait Foo[A] {
  def up[A1 >: A]: Foo[A1]
  def bar1(x: Foo[_ >: A]): Foo[A]
  def bar2[A1 >: A](x: Foo[A1]): Foo[A]
}

object Test {
  def test(a: Foo[Int], b: Foo[Any]) = {
    val x1 = a.up.bar1(b)
    val x2 = a.up.bar2(b)
  }
}

However, type inference works better for bar2 (in Dotty, both are inferred to Foo[Int]):

      val x1: Foo[Any] = a.up[Any].bar1(b);
      val x2: Foo[Int] = a.up[Int].bar2[Any](b);

It'd be good to figure out whether or not this can be fixed since this affects the API of the collections, in 2.13 we currently have in Seq:

def diff(that: Seq[_ >: A]): C

Which means that in the following code, x is inferred to have type Buffer[Any] and not Buffer[Int]:

object Test {
  def test(a: Seq[Int], b: Seq[Any]): Unit = {
    val x = a.toBuffer.diff(b)
  }
}

/cc @adriaanm @szeiger

smarter added a commit to smarter/scala that referenced this issue Jun 5, 2018
Note: it would be cleaner to use a bounded wildcard:

    def intersect(that: Set[_ >: A]): C

Unfortunately that would result in worse type inference, see
scala/bug#10920
smarter added a commit to smarter/scala that referenced this issue Jun 5, 2018
Note: it would be cleaner to use a bounded wildcard:

    def intersect(that: Set[_ >: A]): C

Unfortunately that would result in worse type inference, see
scala/bug#10920
smarter added a commit to smarter/scala that referenced this issue Jun 5, 2018
Note: it would be cleaner to use a bounded wildcard:

    def intersect(that: Set[_ >: A]): C

Unfortunately that would result in worse type inference, see
scala/bug#10920
smarter added a commit to smarter/scala that referenced this issue Jun 5, 2018
Note: it would be cleaner to use a bounded wildcard:

    def intersect(that: Set[_ >: A]): C

Unfortunately that would result in worse type inference, see
scala/bug#10920
@smarter smarter changed the title Worse type inference when writing [A1 >: A](x: Foo[A]) as opposed to (x: Foo[_ >: A]) Worse type inference when writing [A1 >: A](x: Foo[A1]) as opposed to (x: Foo[_ >: A]) Jun 6, 2018
@smarter smarter added this to the 2.13.0-RC1 milestone Jun 13, 2018
@smarter smarter added the fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) label Oct 19, 2018
@adriaanm
Copy link
Contributor

Haven't looked closely, but does this conflict with #10819?

@smarter
Copy link
Member Author

smarter commented Nov 14, 2018

Don't think so, in both cases we should minimize the type variables because they appear in covariant or invariant positions

@adriaanm
Copy link
Contributor

adriaanm commented Nov 14, 2018 via email

@joroKr21
Copy link
Member

Isn't the occurrence as a lower bound in contravariant position?

  def bar1(x: Foo[_ >: A]): Foo[A]

@smarter
Copy link
Member Author

smarter commented Dec 28, 2018

Yes, but it also appears in an invariant position (the A in Foo[A] in the result type), and the logic (at least in Dotty) is to consider that something that appears in positions with different variances as if it appeared in an invariant position.

@joroKr21
Copy link
Member

Ok then at least we know where to look. scalac considers only the formal parameter types to compute the variance of type parameters:
https://github.com/scala/scala/blob/284a9c03c650c76e2b5295fee5c03565bcdf5246/src/compiler/scala/tools/nsc/typechecker/Infer.scala#L550

I tried adding the result type there but it caused breakage elsewhere.

@joroKr21
Copy link
Member

There must be more to the story because this works fine:

scala> case class Fn[A, B](f: A => B)
// defined case class Fn

scala> Fn((s: String) => s.length)
val res0: Fn[String, Int] = Fn(rs$line$2$$$Lambda$1703/752060661@4207852d)

Although A appears in invariant position in the result type (Fn[A, B]) it is still inferred as String from the constraint A <: String which means A is maximized here.

@adriaanm
Copy link
Contributor

adriaanm commented Jan 8, 2019

Can we avoid the existential in the collections in favor of the type param? I think we're out of time to fix the typer for 2.13.

@szeiger szeiger changed the title Worse type inference when writing [A1 >: A](x: Foo[A1]) as opposed to (x: Foo[_ >: A]) Worse type inference when writing (x: Foo[_ >: A]) as opposed to [A1 >: A](x: Foo[A1]) Jan 11, 2019
@szeiger
Copy link

szeiger commented Jan 11, 2019

Yes, this affects diff and intersect. Both had type parameters instead of existentials in 2.12, so there shouldn't be any unexpected side-effects when reverting to that state.

@szeiger
Copy link

szeiger commented Jan 11, 2019

Workaround in scala/scala#7630. Unassigning and rescheduling to 2.14.

@szeiger szeiger removed their assignment Jan 11, 2019
@szeiger szeiger modified the milestones: 2.13.0-RC1, 2.14.0-M1 Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
fixed in Scala 3 This issue does not exist in the Scala 3 compiler (https://github.com/lampepfl/dotty/) infer library:collections minimized typer usability
Projects
None yet
Development

No branches or pull requests

4 participants