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

发布于 2024-12-18 19:50:03 字数 694 浏览 3 评论 0原文

所以,我一直在阅读这篇文章 关于 LiftWeb 中 Box 的使用,这似乎是官方文档的一部分,因为它是通过源代码注释链接的。 我的问题是,为什么 Box/Failure 比实际编码时没有 null 并抛出异常更好,该异常将在顶层被捕获并转换为适当的错误代码/消息。因此,而

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 的内容

So, I've been reading this article about Box usage in LiftWeb which seems so be part of the official documentation as it's linked through the source code comments.
My question is why is Box/Failure preferable to actually coding without null and throwing an Exception that would be caught at top level and transformed into an appropriate error code/message. So instead of

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

why not

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

and have User.find throw something like NotFoundException

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

可是我不能没有你 2024-12-25 19:50:03

想象一下,您有一个方法执行某些可能会失败的操作,例如获取网页。

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)可以为您带来更多好处比抛出异常更有能力决定何时处理问题。

除了例外情况,您只能遍历堆栈并捕获它作为那里的某个点。如果您将故障编码在类型中,您可以随身携带它,只要您愿意,并在您认为最合适的情况下处理它。

Imagine you have a method which does some operation which may potentially fail, for example fetching a web page.

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

If you want to use it, you need to do

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

or similar depending on the situation. Still, it looks somewhat okay to do so. But now imagine you want to do this for a collection of URLs.

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

Okay so this return an empty sequence whenever one of the pages could not be loaded. What if we’d like to receive as many as possible?

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

Now our logic is intermingled with error handling code.

Compare this to the following:

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

Using Option or Box (or Either or scalaz’ Validation) gives you way more power over deciding when to deal with a problem than throwing exceptions ever could.

With exceptions you may only traverse the stack and catch it as some point there. If you encode the failure inside a type, you may carry it around with you as long as you like and deal with it in the situation you think is most appropriate.

伴我老 2024-12-25 19:50:03

如果抛出异常,唯一能做的就是捕获它。因此,如果页面的两个部分抛出异常,您将永远无法到达第二个部分。如果页面的一部分返回一个 Box,而该部分恰好是 Failure,而第二部分不需要第一部分的返回值,那么您可以同时看到这两个部分。更一般地说,Box 有一个 Exception 所缺乏的有用 API。

If you throw an exception, the only thing you can do is catch it. So, if two parts of your page throw an exception, you won't ever get to the second one. If one part of your page returns a Box which happens to be a Failure, and the second part doesn't need the return value of the first one, you can see both. More generally, Box has a useful API which Exception lacks.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文