4

In the following block of code (with both scala 2.11 and 2.12) the method apply does not compile, while applyInlined does.

package blar

trait Bar[T]

class A
class B
class C

trait Exploder[T] {
  // Removing explode and changing Foo so that
  // flatMap takes no param means it will compile
  def explode(product: C): Seq[T]
  val bar: Bar[T]
}

case object Exploder1 extends Exploder[A] {
  def explode(product: C): Seq[A] = ???
  val bar: Bar[A] = ???
}

case object Exploder2 extends Exploder[B] {
  def explode(product: C): Seq[B] = ???
  val bar: Bar[B] = ???
}

object Thing {
  def apply(): Unit = List(Exploder1, Exploder2).foreach {
    case exploder: Exploder[_] =>
      wrapped(exploder)
  }

  def applyInlined(): Unit = List(Exploder1, Exploder2).foreach {
    case exploder: Exploder[_] =>
      flatMap(exploder.explode)(exploder.bar)
  }

  def flatMap[U: Bar](explode: C => TraversableOnce[U]): Unit = ???

  def wrapped[T](exploder: Exploder[T]): Unit =
    flatMap(exploder.explode)(exploder.bar)
}

The error message is

[error] .../src/main/scala/blar/Bar.scala:34:42: type mismatch;
[error]  found   : blar.Bar[_1]
[error]  required: blar.Bar[Object]
[error] Note: _1 <: Object, but trait Bar is invariant in type T.
[error] You may wish to define T as +T instead. (SLS 4.5)
[error]       flatMap(exploder.explode)(exploder.bar)
[error]                                          ^
[error] one error found
[error] (Compile / compileIncremental) Compilation failed
[error] Total time: 4 s, completed 03-Jan-2019 13:43:45
  1. My main question is why? Is this a bug?

As you can see applyInlined only differs in that it has inlined the body of the wrapped method. This means that somehow the extra wrapping of some code in a method has "tricked" the compiler into working.

  1. The other question is can you think of a design/hack that would avoid this kind of thing without making Blar covariant? How can I make the inlined version compile? Can I do it with an asInstanceOf

  2. What is scala inferring the type to be in order to call wrapped without an explicit type param?

samthebest
  • 30,803
  • 25
  • 102
  • 142

1 Answers1

3
  1. I don't know. Experimental evidence shows that it has something to do with the presence / absence of the explicit [Exploder[_]] type annotation on List. Without List[Exploder[_]], the inferred type of the list becomes

    List[Product with Serializable with Exploder[_ >: B with A <: Object]]
    

    and for whatever reason, it somehow messes up the subsequent pattern matching. The B with A lower bound looks a little suspicious to me, but I can't explain why it would interfere with the pattern matching.

  2. No, fortunately, no asInstanecOfs are required. Both of the following variants work fine:

    def applyInlined: Unit = List[Exploder[_]](Exploder1, Exploder2).foreach {
      case exploder: Exploder[t] => {
        flatMap(exploder.explode)(exploder.bar)
      }
    }
    

    same with a separately declared implicit variable (note that now you have some type t to refer to):

    def applyInlined2: Unit = List[Exploder[_]](Exploder1, Exploder2).foreach {
      case exploder: Exploder[t] => {
        implicit val bar: Bar[t] = exploder.bar
        flatMap(exploder.explode)
      }
    }
    

    See Type parameter inference in patterns for more about the [t]-part.

  3. I assume it's some synthetic dummy type _1.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Thanks Andrey, especially for answer 2, now I can inline that useless method. Would be interesting to understand "for whatever reason, it somehow messes up the subsequent pattern matching". – samthebest Jan 04 '19 at 16:40