Как я могу легко определить более сложные PartialFunctions в Scala? - PullRequest
14 голосов
/ 07 июля 2011

PartialFunctions

В Scala, PartialFunction - это, вкратце, функция, которая дополнительно определяет метод isDefinedAt.

Это легко определитьчастичные функции с серией операторов case.Тривиальным примером может быть, например:

scala> val pf: PartialFunction[Int, Unit] = {
     | case 42 => ()
     | }
pf: PartialFunction[Int,Unit] = <function1>

scala> pf.isDefinedAt(42)
res0: Boolean = true

scala> pf.isDefinedAt(0) 
res1: Boolean = false

isDefinedAt автоматически генерируется из списка case s, определяющих частичную функцию.

Context

Платформа Lift использует частичные функции во многих местах, например, чтобы определить, должен ли запрос обрабатываться механизмом Lift или обслуживаться непосредственно из файла на диске, как есть.и иногда я хочу написать оператор case, который соответствует всем входным параметрам, и только позже решить, хочу ли я вернуть значение или нет.Это означает, что исходной серии case s больше не достаточно, чтобы определить, определена ли моя функция с заданным значением или нет

Например, в Lift я хочу добавить правило, что все htmlи htm файлы обслуживаются напрямую, и файлы с расширением «lift» должны обрабатываться.Было бы легко сделать что-то вроде этого:

LiftRules.liftRequest.prepend {
  case Req(path, extension, tpe) => extension match {
    case "html" | "htm" => false
    case "lift" => true
  }
}

К сожалению, в этом случае компилятор считает, что моя частичная функция определена везде, так как первое case всегда совпадает.Это вложенный match, который может не соответствовать всем входящим запросам.И, если запрос не совпадает, выдается MatchError.

Вопрос

Есть ли простой способ заставить компилятор учитывать вложенные операторы match при определении частичной функции,или это единственный способ сделать это для вставки всех вложенных условных выражений, подобных этому?

LiftRules.liftRequest.prepend {
  case Req(path, extension, tpe) if extension == "html" || extension == "htm" => false
  case Req(path, extension, tpe) if extension == "lift" => true
}

В этом примере это в основном выполнимо, но читаемость снижена, и я сталкивался со случаями, когда вставка всех проверок выглядиточень некрасиво.

1 Ответ

23 голосов
/ 07 июля 2011

В этом случае вы можете написать

LiftRules.liftRequest.prepend {
  case Req(path, "html" | "htm", tpe) => false
  case Req(path, "lift", tpe) => true
}

. В более сложных случаях вам потребуется определить собственный экстрактор, который вам придется использовать вместо вложенного оператора case..

object CheckExtension {
  def unapply(ext: String) = ext match {
    case "lift" => Some(true)
    case "html" | "htm" => Some(false)
    case _ => None
  }
}

LiftRules.liftRequest.prepend {
  case Req(path, CheckExtension(valid), tpe) => valid
}

Это будет соответствовать, только если ваша предопределенная функция unapply вернет Some и присвоит значение Some свободной переменной valid.Если unapply возвращает None, совпадение не создается.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...