如何在 Scala 中否定模式匹配中的类型?

How to negate types in pattern match in Scala?

使用此代码,println 将仅针对指定的异常执行。我想知道是否有可能否定该行以使其针对所有其他未指定的异常执行。我知道可以使用 2 个案例,但我想知道是否可以用一个案例完成。

val myHandler: PartialFunction[Throwable, Unit] = {
  case e @ (_: MappingException | _: ParseException | _: SomeOtherException) =>
   println("Got it")
}

AFAIk 你不能用一个匹配来做到这一点,但你可以创建你自己的自定义 Extractor 以防你需要在多个地方复制这个行为。

import scala.reflect.ClassTag

final class Not[A : ClassTag] {
  def unapply(any: Any): Boolean = any match {
    case _: A => false
    case _ => true
  }
}
object Not {
  def apply[A : ClassTag]: Not[A] = new Not
}

你可以这样使用:

final val NotAnInt = Not[Int]

10 match {
  case NotAnInt() => false
  case _ => true
}
// res: Boolean = true

"10" match {
  case NotAnInt() => true
  case _ => false
}
// res: Boolean = true

但是,请记住,这将具有任何类型检查的所有限制,例如无法区分 List[Int] List[String] 由于erasure;并被认为是 bad practice.

我建议研究 typeclass 方法,例如,我相信 Shapeless 提供了一个否定方法。


可以看到代码运行 here.

好吧,您已经确定了可能 更多 可读的方法。

val myHandler: PartialFunction[Throwable, Unit] = {
  case e @ (_: MappingException | _: ParseException | _: SomeOtherException) =>
    throw e
  case _ =>
    println("Got it")
}

这可能是我在实际生产代码中的写法。明智,一目了然

但是你要求一个案例,所以让我们试一试。由于我们要检查多种类型,因此我们需要能够将它们表示为列表。有无数的 Scala 库可以使它更漂亮,但出于我们的目的,我们将自己推出。

trait TList {
  def isMember(x: Any): Boolean
}

object Nil extends TList {
  def isMember(x: Any) = false
}

case class Cons[H : ClassTag](val tail: TList) extends TList {
  def isMember(x: Any) = {
    x match {
      case _: H => true
      case _ => tail.isMember(x)
    }
  }
}

因此我们可以表示经典的 Lisp 风格的单链表,并检查任意 Any 值是否在列表中的任何位置都有类型。现在让我们否定它并编写一个 unapply 方法。

case class NotMember(val types: TList) {
  def unapply(elem: Any): Boolean =
    !types.isMember(elem)
}

然后我们的处理程序看起来像

val test = NotMember(
  Cons[MappingException](Cons[ParseException](Cons[SomeOtherException](Nil)))
)
val myHandler: PartialFunction[Throwable, Unit] = {
  case test() =>
    println("Got it")
}

同样,如果您真的想沿着这条路走下去,您将需要一个库来使类型级别的东西易于管理。但这绝对有可能。唯一的问题是它是否值得您的用例。