为什么在另一个方法中包装方法会停止Scala中的类型不匹配-在模式匹配中的类型参数中使用下划线?

时间:2019-01-03 13:56:55

标签: scala types covariance scalac scala-compiler

在下面的代码块中(具有scala 2.112.12),方法apply不会编译,而applyInlined会编译。

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)
}

错误消息是

[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. 我的主要问题是为什么?这是一个错误吗?

您可以看到applyInlined的不同之处仅在于它内联了wrapped方法的主体。这意味着某种程度上,方法中某些代码的额外包装已“诱使”编译器正常工作。

  1. 另一个问题是,您能想到一种设计/ hack可以避免这种情况吗? 而不会使Blar协变?如何使内联版本编译?我可以用asInstanceOf

  2. 做吗
  3. 在没有显式类型参数的情况下调用wrapped的scala是什么类型的推断?

1 个答案:

答案 0 :(得分:3)

  1. 我不知道。实验证据表明,它与[Exploder[_]]上显式List类型注释的存在/不存在有关。如果没有List[Exploder[_]],列表的推断类型将变为

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

    出于某种原因,它以某种方式弄乱了后续的模式匹配。 B with A的下限对我来说有点可疑,但是我无法解释为什么它会干扰模式匹配。

  2. 不,幸运的是,不需要asInstanecOf。以下两个变体都可以正常工作:

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

    与单独声明的隐式变量相同(请注意,现在您要引用某种类型的t

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

    有关[t]部分的更多信息,请参见Type parameter inference in patterns

  3. 我认为它是某种合成的虚拟类型_1