是否可以在 Scala for expression 中对类型进行模式匹配?

Is it possible to pattern match on type in a Scala for expression?

我正在尝试使用 for 表达式映射选项,但我只想匹配选项的内容是否属于特定类型。我认为可行的是:

for {
  vcs: Mercurial <- maybeVcs
} yield vcs

但这会产生以下编译错误:

<console>:76: error: type mismatch;
 found   : sbtrelease.Mercurial => sbtrelease.Mercurial
 required: sbtrelease.Vcs => ?
                vcs: Mercurial <- get (releaseVcs in Compile)
                               ^

是否可以对 for 表达式中的类型进行模式匹配?

你可以使用丑陋的测试:

for {
  vcs <- maybeVcs
  if vcs.instanceof[Mercurial]
} yield vcs

如果使用 collect 而不是 for 就非常简单了:

trait A
case class B(x: Int) extends A
case class C(y: Int) extends A

val someB: Option[A] = Some(B(2))
val someC: Option[A] = Some(C(2))
val noneA: Option[A] = None
someB.collect { case n: B => n }   // Some(B(2))
someC.collect { case n: B => n }   // None
noneA.collect { case n: B => n }   // None

这个模式匹配不起作用的事实实际上是一个错误(至少它不符合规范)。参见 https://issues.scala-lang.org/browse/SI-900

但是,有一个简单的解决方法。 在某处定义以下对象:

object Id { unapply[T](x:T) = Some(x) }

现在您可以使用 Id(x) 作为匹配所有内容的模式匹配,并将 x 绑定到它匹配的任何内容。所以基本上是一个毫无意义的构造,因为 Id(pattern)pattern.

相同

但是,它有一个作用:Id(...) 中的类型注释不会被解释为类型注释,而是类型模式。因此

for {
  Id(vcs: Mercurial) <- maybeVcs
} yield vcs

会有你想要的效果。 (与 不同的是,整个表达式的类型为 Seq[Mercurial] 而不是 Seq[Vcs]。)