Type failure: List of container with type parameter of Nothing (Dotty works)


#1

I have the very simple test:

object Test {
  sealed trait Op[I,T,O]
  final case class Func[I,T,O](t: I => O) extends Op[I,Nothing,O]
  final case class All[I,T,O](f: List[Op[I,T,O]]) extends Op[I,T,O]


  def f1(i:Int) = i + 1
  def f2(i:Int) = i * 2

  def ff1 = Func(f1)
  def ff2 = Func(f2)

  val ll = List(ff1,ff2)
  val all = All(ll)

  def main(args: Array[String]): Unit = {
    println(all)
  }

}

When using 2.12.6 I get the following error:

val all = All(ll)
type mismatch;
 found   : List[Test.Func[Int,Nothing,Int]]
 required: List[Test.Op[Int,T,Int]]

With Dotty (using https://scastie.scala-lang.org/) it runs without a hitch.
I have tried:

  val ll:List[Op[Int,Nothing,Int]] = List(ff1,ff2)

But still get the same error. And when I use:

al ll:List[Op[Int,_,Int]] = List(ff1,ff2)

I get:

no type parameters for method apply: (f: List[Test.Op[I,T,O]])Test.All[I,T,O] in object All exist so that it can be applied to arguments (List[Test.Op[Int, _, Int]])
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : List[Test.Op[Int, _, Int]]
 required: List[Test.Op[?I,?T,?O]]

type mismatch;
 found   : List[Test.Op[Int, _, Int]]
 required: List[Test.Op[I,T,O]]

Am I correct in assuming that the type List[Op[Int,Nothing,Int]] should be the inferred type?
In other words is Dotty correct (can I expect this behavior in the future)?
And last, but not least, I circumvented this by changing the definition to:

  final case class All[I,T,O](f: List[Op[I,_,O]]) extends Op[I,T,O]

Is this Ok, or should I do something else (safer)?

TIA


#2

I have dome some more experimentation and found that the following
two combinations work in Scala 2.12.6 (and dotty):

sealed trait Op[I,T,O]
  final case class Func[I,T,O](t: I => O) extends Op[I,Null,O] 
  final case class All[I,T,O](f: List[Op[I,T,O]]) extends Op[I,T,O]

and

sealed trait Op[I,T,O]
  final case class Func[I,T,O](t: I => O) extends Op[I,Any,O]
  final case class All[I,T,O](f: List[Op[I,T,O]]) extends Op[I,T,O]

Would the use of Null here also be acceptable?

TIA


#3

Why does Func need a type parameter T?


#4

@Jasper-M you are correct, it is not necessary. I was using the T to model the
intermediate result of composing 2 Func. Now I cannot for the life of me find
the need for this in my code (simple DSL compiler). Going to look at this
more carefully.

But I still wonder …

Thanks


#5

Sure, it is still a good question.