В частичной функции сопоставления с шаблоном, как заставить isDefined возвращать false для недопустимых входных данных, которые не могут быть включены в шаблон case?

В частичной функции, реализованной с сопоставлением шаблонов, как заставить isDefined возвращать 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 для недопустимых входных данных. Например, для a, которое decode(a) оценивается как None, decodeList.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)
}

person Shreck Ye    schedule 14.03.2020    source источник


Ответы (2)


Вы можете сделать это, определив пользовательский объект экстрактора, например.

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
}
person Alexey Romanov    schedule 14.03.2020
comment
Не могли бы вы также опубликовать краткую реализацию def decodeList(list: Json): Option[List[A]] для сравнения? - person Shreck Ye; 14.03.2020
comment
Добавил. Должны быть лучшие способы сделать это, но этот остается близким к вашему коду. - person Alexey Romanov; 15.03.2020

Технически вы можете переопределить isDefinedAt, напрямую определив PartialFunction вот так

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 предоставляет по умолчанию.

person Mario Galic    schedule 14.03.2020