为什么在LiftWeb/Scala中使用Box/Option而不是Exeption?

ux6nzvsh  于 2022-11-09  发布在  Scala
关注(0)|答案(2)|浏览(204)

所以,我一直在阅读关于LiftWeb中Box用法的this article,它似乎是官方文档的一部分,因为它通过源代码注解进行了链接。我的问题是,为什么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的代码

scyqe7ek

scyqe7ek1#

假设您有一个方法,它执行一些可能会失败的操作,例如获取一个网页。

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

与抛出异常相比,使用OptionBox(或Either或scalaz‘Validation)可以使您在决定何时处理问题方面拥有更大的权力。
在有例外的情况下,您只能遍历堆栈并在那里的某个点捕获它。如果您将失败编码到类型中,您可以随身携带它,只要您愿意,并在您认为最合适的情况下处理它。

bzzcjhmw

bzzcjhmw2#

如果抛出异常,唯一能做的就是捕捉它。因此,如果页面的两部分抛出异常,您将永远无法转到第二部分。如果页面的一部分返回Box,而该Box恰好是Failure,而第二部分不需要第一部分的返回值,则可以同时看到两者。更广泛地说,Box具有Exception所缺少的有用API。

相关问题