Skip to content

Dependent function types do not act contravariantly in their parameter types #6357

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

Closed
smarter opened this issue Apr 22, 2019 · 2 comments
Closed

Comments

@smarter
Copy link
Member

smarter commented Apr 22, 2019

For example:

class A
class B extends A
object Test {
  val f1: A => Any = a => a
  // This compiles
  val f2: B => Any = f1

  val f3: (a: A) => Any = a => a

  // But this doesn't
  val f4: (a: B) => Any = f3
}

Running with -explain-types we get:

-- [E007] Type Mismatch Error: try/df.scala:11:26 ------------------------------
11 |  val f4: (a: B) => Any = f3
   |                          ^^
   |Found:    (a: A) => Any(Test.f3)
   |Required: (a: B) => Any
   |Constraint(
   | uninstVars = ;
   | constrained types =
   | bounds =
   | ordering =
   |)
   |Subtype trace:
   |  ==> (a: A) => Any(Test.f3) <:< (a: B) => Any
   |    ==> A => Any <:< B => Any LoApprox
   |      ==> scala.type(scala) <:< scala.type(scala)
   |      <== scala.type(scala) <:< scala.type(scala)   = true
   |      ==> B <:< A
   |        ==> A <:< A LoApprox
   |        <== A <:< A LoApprox  = true
   |      <== B <:< A   = true
   |      ==> Any <:< Any
   |      <== Any <:< Any   = true
   |    <== A => Any <:< B => Any LoApprox  = true
   |    ==> (v1: A): Any <:< (a: A): Any   frozen
   |      ==> Any <:< Any   frozen
   |      <== Any <:< Any   frozen = true
   |    <== (v1: A): Any <:< (a: A): Any   frozen = true
   |    ==> hasMatchingMember((a: A) => Any(Test.f3) . apply, (a: B): Any), member = (v1: A): Any
   |      ==> (v1: A): Any <:< (a: A): Any   frozen
   |        ==> Any <:< Any   frozen
   |        <== Any <:< Any   frozen = true
   |      <== (v1: A): Any <:< (a: A): Any   frozen = true
   |      ==> (v1: A): Any <:< (a: B): Any
   |      <== (v1: A): Any <:< (a: B): Any   = false
   |    <== hasMatchingMember((a: A) => Any(Test.f3) . apply, (a: B): Any), member = (v1: A): Any = false
   |  <== (a: A) => Any(Test.f3) <:< (a: B) => Any   = false

In particular note (v1: A): Any <:< (a: B): Any = false, this makes sense because when overriding a method in Scala, refining its term parameters contravariantly is not allowed, but dependent function types are encoded with a refined apply method and this is not the behavior we want for them. It seems like we need some special-casing of dependent methods here. @odersky WDYT ?

@odersky
Copy link
Contributor

odersky commented Apr 22, 2019

That's a language enhancement, not a bug. Dependent function types are specified to be refinements of plain function types, which implies they are non-variant in their parameters. Changing this would require a different spec.

@smarter
Copy link
Member Author

smarter commented Apr 29, 2021

Fixed by #12211

@smarter smarter closed this as completed Apr 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants