Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot infer contravariant Nothing type parameter

Consider the following snippet:

trait X[-T]
object Y extends X[Nothing]
def a[T](x: X[T]): X[T] = x
a(Y)

Compilation of the above (2.12.3) fails with:

type mismatch;
found   : Y.type
required: X[T]
   a(Y)
     ^

This compiles fine if:

  • a different type than Nothing is used (e.g. object Y extends X[String])
  • the method a doesn't use T in its return type (e.g. def a[T](x: X[T]): Unit = {})
  • the type parameter for a is explicitly given (i.e. a[Nothing](Y))
  • T is covariant, not contravariant (also fails if it's invariant)

Is this some special case in the compiler for Nothing?

As an "interesting" work-around, the following seems to work fine:

trait X[-T]
object Y extends X[Nothing]
def a[T, U <: T](x: X[T]): X[U] = x
a(Y)
like image 622
adamw Avatar asked Oct 09 '17 09:10

adamw


1 Answers

I will try to explain the code line by line

Line 1: trait X[-T] -> trait X is contravariant in type T. So you can replace any variable of type X[T] with its subtype. In case of contravariant types, Z[A] is subtype of Z[B] if B is subtype of A.

Line 2: object Y extends X[Nothing] -> object Y is of type X[Nothing]. Please note that Nothing is subtype of all other types.

Line 3: def a[T](x: X[T]): X[T] = x -> define an expression which takes arguments of type X[T]. Because trait X is contravariant in type T, you can also pass subtypes of X[T] i.e. X[N] such that T is subtype of N

Line 4: a(Y) -> Call expression 'a' with argument of type X[Nothing]. Since the compiler does not know the type of argument of 'a', it can not decide if X[Nothing] is subtype of X[T]. There are multiple ways to resolve this

Solution 1: `a[Nothing]` -> explicitly defining the type

Solution 2: `tait X[+T]` -> make X covariant in type T. In this case Z[A] is subtype of Z[B] if A is subtype of B. This will work because Nothing is subtype of any other type
like image 125
AB9KT Avatar answered Sep 23 '22 09:09

AB9KT