LaloInDublin
LaloInDublin

Reputation: 5449

Why does Scala sometimes ignore types that are clearly defined?

so here's the problem I keep running into various situations with Scala - it seemingly ignores the implied type, even when the situation is clear. Granted this could be my understanding I admit, but when it comes to underscore placeholders I keep running into trouble. For example below (this is fictional just to prove the point).The 2nd position of trait X has to be <:X[,] of some kind. There's no ambiguity here - so anywhere that scala sees this position, regardless of how weak it's coded - the contact is X and I should have access to functions like "doesX". Isn't that indisputable? No matter how poorly I deal with that position in the code, I must at least get X. Why does Scala constantly ignore this fact when you get deep into the type system? Any pointers would be appreciated, thank you!

object TestRun extends App {

  trait X[T, Y<:X[_,_]] {
    def doesX:Unit
    def providesY:Y
  }

  class Test extends X[Int,Test]{
    override def doesX: Unit = println("etc..")
    def providesY:Test =  new Test
  }

  val a:X[_,_] = new Test //yes I know I could define a better here, its just to demo. I shouldn't have to explicitly relabel the 2nd _ as _<:X[_,<:X[ etc..
  val b = a.providesY //clearly this has to be at least a (something) WITH X, but scala claims this as "Any"

  b.doesX //error won't compile!!

  //trait

}

Upvotes: 3

Views: 68

Answers (1)

Michael Zajac
Michael Zajac

Reputation: 55569

When you write:

val a: X[_, _] = new Test
            ^
      //  This is treated as if the type parameter is Any, for the most part

You are telling the compiler that a is an X, where you don't care what its type parameters are. That is, the unbounded wildcard _ is assumed to have an upper-bound of Any, and that's it.

providesY uses the second type parameter of X to determine its return type, but for a the compiler was told that to discard it. So b is just an Any. This is easier to see using the REPL:

scala> val a: X[_, _] = new Test
a: X[_, _] = Test@27abe2cd

scala> val b = a.providesY
b: Any = Test@f5f2bb7

Therefore, b.doesX fails to compile because the compiler now thinks it is Any. The simple solution is not to use wild cards for types (or any existential types in general, most of the time you do not want this).

scala> val a: X[Int, Test] = new Test
a: X[Int,Test] = Test@1134affc   

scala> val b = a.providesY
b: Test = Test@6fc6f14e

scala> b.doesX
etc..

Or you could simply leave off the type annotation, and let the compiler infer the correct type.

Upvotes: 6

Related Questions