1

Given:

class Invar[T]
trait ExtendsAnyref extends AnyRef
def f(a: Invar[ExtendsAnyref]) = {}

The following is erroneous

scala> val x: Function1[Invar[_ <: AnyRef], Unit] = f
<console>:13: error: type mismatch;
 found   : Invar[ExtendsAnyref] => Unit
 required: Invar[_ <: AnyRef] => Unit
       val x: Function1[Invar[_ <: AnyRef], Unit] = f
                                                    ^

Why?

I understand that in Scala, generic types have by default nonvariant subtyping. Thus, in the context of this example, instances of Invar with different type parameters would never be in a subtype relationship with each other. So an Invar[ExtendsAnyref] would not be usable as a Invar[AnyRef].

But I am confused about the meaning of _ <: AnyRef which I understood to mean "some type below AnyRef in the type hierarchy." ExtendsAnyref is some type below AnyRef in the type hierarchy, so I would expect Invar[ExtendsAnyref] to conform to Invar[_ <: AnyRef].

I understand that function objects are contravariant in their input-parameter types, but since I use Invar[_ <: AnyRef] rather than Invar[AnyRef] I understood, apparently incorrectly, the use of the upper bounds would have the meaning "Invar parameterized with Anyref or any extension thereof."

What am I missing?

Adam Mackler
  • 1,980
  • 1
  • 18
  • 32
  • "Invar parameterized with Anyref or any extension thereof." This is correct. And it is also the answer to your question: you want a function that takes `Invar[AnyRef]`, but instead are creating a function that takes what you wrote above. That's a different type. – Dima Jun 12 '16 at 16:40
  • "you want a function that takes `Invar[AnyRef]`..." But I don't want that. I want a function that takes `Invar` parameterized with **any subclass of `AnyRef`**. You say that what I wrote excludes the "subclass of" part, but if the upper limit `_ <:` doesn't do that, what does it do? – Adam Mackler Jun 12 '16 at 16:53
  • Ok, the other way around: you want a function " that takes Invar parameterized with any subclass of AnyRef", but are giving it a function that takes `Invar[AnyRef]` .That's a different type. The property of being a different type is commutative, so it does not matter: if A is not the same type as B, then B is not the same type as A. – Dima Jun 12 '16 at 17:57
  • You lost me. Where am I "giving it a function that takes `Invar[AnyRef]`?" The function `f()` takes an argument of type `Invar[ExtendsAnyref]`, and in fact the error message says `found : Invar[ExtendsAnyref] => Unit`. What function are you talking about that takes `Invar[AnyRef]`? – Adam Mackler Jun 12 '16 at 18:06
  • Yes, a typo, sorry. You give it `[ExtendsAnyRef]` it wants `[_ extends AnyRef]`. You keep missing the point though: doesn't matter what exact types they are, what matters is that they are different. – Dima Jun 12 '16 at 18:08

1 Answers1

1

When you write

val x: Function1[Invar[_ <: AnyRef], Unit] = ...

it means x must accept any Invar[_ <: AnyRef]. That is, it must accept Invar[AnyRef], Invar[String], etc. f obviously doesn't: it only accepts Invar[ExtendsAnyref].

In other words, you need to combine your last two paragraphs: because functions are contravariant in argument types, for Function1[Invar[ExtendsAnyref], Unit] to conform to Function1[Invar[_ <: AnyRef], Unit] you'd need Invar[_ <: AnyRef] to conform to Invar[ExtendsAnyref], not vice versa.

If you

want a function that takes Invar parameterized with any subclass of AnyRef

this can be written as Function1[Invar[A], Unit] forSome { type A <: AnyRef }. However, I don't believe there is anything useful you could do with an object of this type, because 1) the only thing you can do with a function is to apply it to an argument, but 2) you don't know what arguments this function accepts.

Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487