Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bizzare type inference limitation - multiple type params

Why does this not compile?

trait Lol[A, SA] {
  def flatMap[B, SB](f: A => Lol[B, SB]): Lol[B, SB] = ???
}

val p1: Lol[Int, String] = ???
val p2: Lol[Double, Nothing] = ???

val p5 = p1.flatMap(_ => p2)

Result:

found   : Int => Lol[Double,Nothing]
required: Int => Lol[Double,SB]
   val p5 = p1.flatMap(_ => p2)
                         ^    

Things start to compile when either:

  • type params of flatMap invocation are explicit
  • SA is covariant (wtf?)
  • some other type than Nothing is used in p2 (e.g. Null)
  • SB does not occur in return type of flatMap or occurs in covariant position of that return type (e.g. return type is Option[SB])

The above workarounds are however not acceptable for me.

like image 333
ghik Avatar asked Aug 19 '15 13:08

ghik


2 Answers

@retronym's commentary on SI-9453 explains the behaviour you're seeing. Here's a workaround of sorts ...

We can synthesize a type equivalent to Nothing which won't cause the typer to retract the inference solution,

type ReallyNothing = Nothing { type T = Unit }

ie. Nothing with a dummy refinement. Now with the example of the question,

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait Lol[A, SA] {
  def flatMap[B, SB](f: A => Lol[B, SB]): Lol[B, SB] = ???
}

val p1: Lol[Int, String] = ???
val p2: Lol[Double, ReallyNothing] = ???

val p5 = p1.flatMap(_ => p2)

// Exiting paste mode, now interpreting.

scala.NotImplementedError: an implementation is missing
  at scala.Predef$.$qmark$qmark$qmark(Predef.scala:225)
  ... 37 elided
like image 197
Miles Sabin Avatar answered Nov 13 '22 12:11

Miles Sabin


In order to better understand your problem, it can be simplified as:

class Lol[A]
def foo[B](f: Lol[B]) = f
foo(new Lol[Nothing])

which gives you the following compilation error:

error: type mismatch;
 found   : Lol[Nothing]
 required: Lol[B]
Note: Nothing <: B, but class Lol is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)

A possible solution is to update the snippet like that:

def foo[B <: Lol[_]](f: B) = f

Back to your original code:

trait Lol[A, SA] {
  def flatMap[B <: Lol[_,_]](f: A => B): B = ???
}
like image 44
Federico Pellegatta Avatar answered Nov 13 '22 12:11

Federico Pellegatta