Почему Box / Option вместо исключения в LiftWeb / Scala? - PullRequest
2 голосов
/ 30 ноября 2011

Итак, я читал эту статью об использовании Box в LiftWeb, который, похоже, является частью официальной документации, поскольку он связан с комментариями исходного кода. Мой вопрос: почему Box / Failure предпочтительнее, чем собственно кодирование без нуля и выдача исключения, которое будет перехвачено на верхнем уровне и преобразовано в соответствующий код ошибки / сообщение. Так что вместо

case "user" :: "info" :: _ XmlGet _ =>
  for {
    id <- S.param("id") ?~ "id param missing" ~> 401
    u <- User.find(id) ?~ "User not found"
  } yield u.toXml

почему бы и нет

case "user" :: "info" :: _ XmlGet _ => User.find(S.param("id").openOrThrow(
    new IllegalArgumentException("idParamMissing"))).toXml

и User.find бросить что-то вроде NotFoundException

Ответы [ 2 ]

2 голосов
/ 01 декабря 2011

Представьте себе, что у вас есть метод, который выполняет какую-то операцию, которая потенциально может дать сбой, например, получение веб-страницы.

def getUrl(url: String): NodeSeq = {
   val page = // ...
   // ...
   if (failure) throw new PageNotFoundException()
   page
}

Если вы хотите использовать его, вам нужно сделать

val node = try {
  getUrl(someUrl)
} catch {
  case PageNotFoundException => NodeSeq.Empty
}

или аналогичный в зависимости от ситуации. Тем не менее, это выглядит несколько хорошо, чтобы сделать это. Но теперь представьте, что вы хотите сделать это для набора URL-адресов.

val urls = Seq(url1, ...)
val nodeseqs: Seq[NodeSeq] = try {
  urls.map(getUrl)
} catch {
  case PageNotFoundException => Seq.Empty
}

Хорошо, так что это возвращает пустую последовательность, когда одна из страниц не может быть загружена. Что если мы хотим получить как можно больше?

val urls = Seq(url1, ...)
val nodeseqs: Seq[NodeSeq] = urls.map { url =>
  try {
    getUrl(url)
  } catch {
    case PageNotFoundException => NodeSeq.Empty
  }
}

Теперь наша логика перемешана с кодом обработки ошибок.

Сравните это со следующим:

def getUrl(url: String): Box[NodeSeq] = {
  val page = // ...
  // ...
  if (failure) Failure("Not found")
  else Full(page)
}

val urls = Seq(url1, ...)
val nodeseqs: Seq[Box[NodeSeq]] = urls.map(getUrl(url)).filter(_.isDefined)
// or even
val trueNodeseqs: Seq[NodeSeq] = urls.map(getUrl(url)).flatten

Использование Option или Box (или Either или scalaz ’Validation) дает вам гораздо больше возможностей для принятия решения о том, когда решать проблему, чем когда бы вы ни создавали исключения.

За исключением, вы можете только пройти через стек и поймать его как некоторую точку там. Если вы кодируете ошибку внутри типа, вы можете носить ее с собой столько времени, сколько пожелаете, и иметь дело с ней в ситуации, которую вы считаете наиболее подходящей.

2 голосов
/ 30 ноября 2011

Если вы выбрасываете исключение, единственное, что вы можете сделать, это поймать его.Таким образом, если две части вашей страницы выдают исключение, вы никогда не попадете на вторую.Если одна часть вашей страницы возвращает Box, который оказывается Failure, а вторая часть не нуждается в возвращаемом значении первой, вы можете увидеть оба.В целом, Box имеет полезный API, которого нет у Exception.

...