在模式匹配部分函数中,如何使 isDefined return false 对于不能包含在 case 模式中的无效输入?

In a pattern matching partial function, how to make isDefined return false for invalid inputs that can't be included in the case pattern?

在用模式匹配实现的部分函数中,如何使isDefined return false 不能包含在case模式中的无效输入?

例如,我有以下 decodeList 部分函数:

case class Arr(items: List[Json]) extends Json
def decode(data: Json): Option[A]
def decodeList: PartialFunction[Json, List[A]] = {
  case Json.Arr(items) =>
    val options = items map decode
    if (options forall (_.isDefined)) options map (_.get)
    else throw new Error // the partial function should be undefined here
}

我想以某种方式更改代码,以便 decodeList.isDefinedAt 对无效输入求值为 false。例如,对于 decode(a) 计算为 NoneadecodeList.isDefinedAt(Json.Arr(List(a))) 应计算为 false.

或者从另一个角度来看,如果我尝试像下面的代码那样在 case 模式中包含条件,我应该把 val options = items map decode 定义放在哪里,这样它就可以被 case 模式和阻止?

def decodeList: PartialFunction[Json, List[A]] = {
  case Json.Arr(items) if (options forall (_.isDefined)) => 
   options map (_.get)
}

您可以通过定义自定义提取器对象来执行此操作,例如

object Options {
  def unapply(items: List[Json]) = Some(items map decode)
}

def decodeList: PartialFunction[Json, List[A]] = {
  case Json.Arr(Options(options)) if (options forall (_.isDefined)) => 
   options map (_.get)
}

这不是特别方便,但我不知道更好的方法。

当然,我建议实际定义 def decodeList(list: Json): Option[List[A]],它更适合 decode,并且不需要这样的解决方法;然后 Function.unlift(decodeList) 如果你需要 PartialFunction.

def decodeList(list: Json) = list match {
  case Json.Arr(items) => 
    val options = items map decode
    if (options forall (_.isDefined)) Some(options map (_.get)) else None
  case _ => None
}

从技术上讲,您可以像这样直接定义 PartialFunction 来覆盖 isDefinedAt

def decodeList: PartialFunction[Json, List[A]] = new PartialFunction[Json, List[A]] {
  override def apply(json: Json): List[A] = json match {
    case Json.Arr(items) =>
      val options = items map decode
      options map (_.get)
  }

  override def isDefinedAt(json: Json): Boolean = json match {
    case Json.Arr(items) =>
      val options = items map decode
      options forall (_.isDefined)
  }
}

然而,这与 isDefinedAt 编译器将由 default 提供的类型不同。