Ryoichiro Oka
Ryoichiro Oka

Reputation: 1997

Identifying two type wildcards as identical

Suppose we define an interface as:

trait Foo[A] {
  val value: A
  def perform(v: A): Unit
}

and compile this code:

val n: Foo[_] = null
n.perform(n.value)

It looks perfect... but we get a mysterious error:

error: type mismatch;
found   : n.value.type (with underlying type _$1)
required: _$1
n.perform(n.value)
            ^

So... why does this happen? I know how to get around this; I am just curious. Thank you!

Upvotes: 4

Views: 117

Answers (2)

Jatin
Jatin

Reputation: 31724

Lets first look at Foo[X] forSome {type X}. It means: For type of all things for which there is some T such that they are of type Foo[T]

Note T is not explicitly mapped to some type. So it is some unknown type.

Compiling with -Xprint:all gives some more info

jatinpuri@jatin:~/Desktop$ scalac -Xprint:all T.scala 
[[syntax trees at end of                    parser]] // T.scala
package <empty> {
    ...
    abstract trait Foo[A] extends scala.AnyRef {
      val value: A;
      def perform(v: A): Unit
    };
    val n: Foo[_$1] forSome { 
      <synthetic> type _$1
    } = null;
    n.perform(n.value)
  }
}

So n.value returns type _$1. But n.perform() also expects a wild type. And two unknown types cant be same. To explain further:

scala> trait Foo[A] {
     |   val value: A
     |   def perform(v: A): Unit
     | type i = A
     | }
defined trait Foo


scala> type I = Foo[X] forSome {type X}
defined type alias I

scala> val n : I = null;
n: I = null

scala> n.value:(I#i)
<console>:16: error: type mismatch;
 found   : n.value.type (with underlying type X)
 required: X
              n.value:(I#i)

I#i is X. But n.value returns something else

Upvotes: 3

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44007

You are not using a raw type here, you are using a wildcard type. This is similar to Java's

List<?> list = null;
list.add(list.get(0));

It does not compile as the compiler works in several steps:

  1. The value n.value is returned, the compiler marks the value to represent a wildcard.
  2. The method n.perform is executed where the compiler only knows that the argument is another wildcard.

Two wildcards are however not necessarily identical. Instead, there are two wildcards issued. In order to make this call, you would need to apply the so-called get-and-put principle. The get an put principle basically implies that you temporarly name the wildcard type such that the Scala compiler can infer that both wildcards are identical. Doing so, the following code compiles:

val n: Foo[_] = null
def rebox[A](x: Foo[A]) = x.perform(x.value)
rebox(n)

and throws a NullPointerException.

Upvotes: 3

Related Questions