方法依赖性和错误处理

发布于 2024-07-30 20:59:45 字数 894 浏览 2 评论 0原文

我正在 Lift 中制作一个小问题管理系统来学习 Scala 和 Lift。

我有一个显示属于项目的单个问题的视图。 在将数据绑定到视图模板之前,我想检查是否拥有所需的所有数据,因此我想具体检查:

  • 已提供项目 ID 参数
  • 项目存在且具有提供的项目 ID
  • 已提供问题 ID 参数
  • 提供的问题 ID 存在问题

并且这些问题需要按顺序进行评估,所以如果我现在用我目前对 Scala 的理解来编写它,我会执行以下操作:

class Project {
    def findByID(xhtml:NodeSeq): NodeSeq = 
        param("projectID") match {
            case Full(projectID) =>
                Project.findByID(projectID) match {
                    case Full(projectID) =>
                        param("issueID") match {
                            ....
                        }
                    case _ => Text("Project does not exist")
                }
            case _ => Text("projectIDNotSupplied")
        }
}

所以我想知道是否有一种更简单的方法执行这个? 我认为 for 表达式可能能够执行类似的操作。 请注意,Project.findByID 返回一个 Box[Project]。

I'm making a small issue management system in Lift to learn both Scala and Lift.

I have a view that displays a single issue belonging to a project. Before I bind data to the view template, I want to check that I have all data required, so I want to specifically check that:

  • A project ID param has been supplied
  • A Project exists with the supplied project ID
  • An issue ID param has been supplied
  • An Issue exists with the supplied issue ID

And these need to be evaluated in order, so if I was to write it now with my current understanding of Scala, I would do the following:

class Project {
    def findByID(xhtml:NodeSeq): NodeSeq = 
        param("projectID") match {
            case Full(projectID) =>
                Project.findByID(projectID) match {
                    case Full(projectID) =>
                        param("issueID") match {
                            ....
                        }
                    case _ => Text("Project does not exist")
                }
            case _ => Text("projectIDNotSupplied")
        }
}

So I'm wondering if there is an easier way to perform this? I think that a for expression might be able to perform something similar. Be aware that Project.findByID returns a Box[Project].

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

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

发布评论

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

评论(3

萌辣 2024-08-06 20:59:48

我不了解 Lift,因此无法回答任何与 Lift 相关的问题。 然而,我确实想出了一种方法来解决您的一个问题,即如何编写一系列检查然后操作操作,而不诉诸嵌套模式匹配。

这里使用的主要数据类型是 Option,但我相信它会很容易适应您的需求。 我们想要在这里完成的是执行一系列

  1. 检查条件
  2. ,如果成功,
  3. 则继续终止,否则返回一些内容。

一旦遇到 None,代码就会执行一种短路操作,以便在操作序列返回时保留返回值。 使用时,从 Option 开始,如果 Option 是 Some,则写入“ifSome”,如果 Option 是 None,则写入“ifNone”,继续直到序列完成。 如果在序列中的任何点遇到 None,则从“isNone”的按名称调用参数返回的选项将被保留,并在调用最终“toOption”时返回。 使用“toOption”返回实际的选项结果。

查看“main”中的示例了解一些用例。 祝你好运!

object Options {

  class RichOption[A](a: Option[A]) {

    def ifSome[B](f: A => Option[B]): RichOption[B] = a match {
      case Some(x) => new RichOption(f(x))
      case None => this.asInstanceOf[RichOption[B]]
    }

    def ifNone[B](f: => Option[B]): RichOption[B] = a match {
      case Some(_) => this.asInstanceOf[RichOption[B]]
      case None => new RichNone(f)
    }

    def toOption[A] = a
  }

  class RichNone[A](a: Option[A]) extends RichOption[A](a) {

    override def ifSome[B](f: A => Option[B]): RichOption[B] = this.asInstanceOf[RichOption[B]]

    override def ifNone[B](f: => Option[B]): RichOption[B] = this.asInstanceOf[RichOption[B]]
  }

  implicit def option2RichOption[A](a: Option[A]): RichOption[A] = new RichOption(a)

  def main(args: Array[String]) {
    println(Some("hello") ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(HELLO)
    println(Some("hello") ifNone(Some("empty")) toOption) // prints Some(hello)
    println(Some("hello") ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) toOption) // prints Some(HELLO)
    println(Some("hello") ifNone(Some("empty")) ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(HELLO)
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) toOption) // prints None
    println((None: Option[String]) ifNone(Some("empty")) toOption) // prints Some(empty)
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) toOption) // prints Some(empty)
    println((None: Option[String]) ifNone(Some("empty")) ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(empty)
    println(Some("hello world") ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(11)
    println(Some("hello world") ifSome(_ => None) ifNone(Some("goodbye world")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(goodbye world)
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(empty)
    println((None: Option[String]) ifSome(_ => None) ifNone(Some("goodbye world")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(goodbye world)
  }
}

I don't know Lift so I can't answer any Lift-specific questions. I did, however, came up with a way to solve one of your problem which is how to write a sequence of check-then-operate actions without resorting to nested pattern matching.

The main datatype in use here is Option, but I am sure it will be quite easy to adapt to your needs. What we want to accomplish here is to do a sequence of

  1. check condition
  2. continue if successful
  3. terminate and return something otherwise

The code does a kind of short circuit once it encounters a None so the return value is retained when the sequence of actions returns. To use, starts with an Option then write "ifSome" if Option is a Some and "ifNone" if Option is a None, continue until the sequence is finished. If a None is encounter at any point in the sequence, the Option returned from call-by-name parameter of "isNone" is retained and returned when the final "toOption" is called. Use "toOption" to get back the actual Option result.

Check out the example in "main" for some use cases. Good luck!

object Options {

  class RichOption[A](a: Option[A]) {

    def ifSome[B](f: A => Option[B]): RichOption[B] = a match {
      case Some(x) => new RichOption(f(x))
      case None => this.asInstanceOf[RichOption[B]]
    }

    def ifNone[B](f: => Option[B]): RichOption[B] = a match {
      case Some(_) => this.asInstanceOf[RichOption[B]]
      case None => new RichNone(f)
    }

    def toOption[A] = a
  }

  class RichNone[A](a: Option[A]) extends RichOption[A](a) {

    override def ifSome[B](f: A => Option[B]): RichOption[B] = this.asInstanceOf[RichOption[B]]

    override def ifNone[B](f: => Option[B]): RichOption[B] = this.asInstanceOf[RichOption[B]]
  }

  implicit def option2RichOption[A](a: Option[A]): RichOption[A] = new RichOption(a)

  def main(args: Array[String]) {
    println(Some("hello") ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(HELLO)
    println(Some("hello") ifNone(Some("empty")) toOption) // prints Some(hello)
    println(Some("hello") ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) toOption) // prints Some(HELLO)
    println(Some("hello") ifNone(Some("empty")) ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(HELLO)
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) toOption) // prints None
    println((None: Option[String]) ifNone(Some("empty")) toOption) // prints Some(empty)
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) toOption) // prints Some(empty)
    println((None: Option[String]) ifNone(Some("empty")) ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(empty)
    println(Some("hello world") ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(11)
    println(Some("hello world") ifSome(_ => None) ifNone(Some("goodbye world")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(goodbye world)
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(empty)
    println((None: Option[String]) ifSome(_ => None) ifNone(Some("goodbye world")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(goodbye world)
  }
}
梦情居士 2024-08-06 20:59:46

我不了解 Lift,但我在它的实现中看到了一些东西。 其中之一是失败方法:?~?~!。 我不确定如何使用它们,但它似乎很有用。 另一个是open_!,用于抛出异常。

现在,Box 支持 map、flatMap、filter 和 foreach,因此它可以在 for 理解中完全使用:

for(projectID <- param("projectID");
    if Project.findByID(projectID).isDefined;
    issueID <- param("issueID");
    if Issue.findByID(issueID).isDefined)
yield ...

这不会给您带来错误消息。 对于这些,我猜测我提到的方法或其他类似 ~> 的方法可能会提供答案。

I don't know Lift, but there are a couple of things I have seen in it's implementation. One of them is the failure methods: ?~ and ?~!. I'm not sure exactly how one might go about using them, but it seems to be useful. Another is open_!, to throw exceptions.

Now, a Box support map, flatMap, filter and foreach, so it can be fully used inside a for comprehension:

for(projectID <- param("projectID");
    if Project.findByID(projectID).isDefined;
    issueID <- param("issueID");
    if Issue.findByID(issueID).isDefined)
yield ...

This won't get you the error messages. For those, I'm guessing the methods I mentioned, or others like ~>, might provide the answer.

暮光沉寂 2024-08-06 20:59:46

抱歉,我来晚了,但正如丹尼尔所说,你确实可以使用 Lift's Box 和 ?~ 来做这样的事情。 例如:

import net.liftweb.util.Box
import net.liftweb.util.Box._

class Project {
  def findByID(xhtml:NodeSeq): NodeSeq = 
    (for {
      projectID <- param("projectID") ?~ "projectIDNotSupplied"
      project <- Project.findById(projectID) ?~ "Project does not exist"
      issueID <- param("issueID") ?~ "issueIDNotSupplied"
      ...
    } yield {
      ...
    }) match {
      case Full(n) => n
      case Failure(msg, _, _) => Text(msg)
      case _ => Text("fail")
    }
}

?~ 的作用是将空框转换为带有给定字符串错误消息的失败框,但对满(成功)框不执行任何操作。 因此,如果一切成功,findByID 的返回值将为 Full,否则返回 Failure(带有给定的错误消息)。 如果您希望失败链接起来,请使用 ?~! 反而。

Sorry I'm so late to the show, but as Daniel says you can indeed use Lift's Box and ?~ to do something like this. For example:

import net.liftweb.util.Box
import net.liftweb.util.Box._

class Project {
  def findByID(xhtml:NodeSeq): NodeSeq = 
    (for {
      projectID <- param("projectID") ?~ "projectIDNotSupplied"
      project <- Project.findById(projectID) ?~ "Project does not exist"
      issueID <- param("issueID") ?~ "issueIDNotSupplied"
      ...
    } yield {
      ...
    }) match {
      case Full(n) => n
      case Failure(msg, _, _) => Text(msg)
      case _ => Text("fail")
    }
}

What ?~ does is turn an Empty Box into a Failure Box with the given String error message, but does nothing to a Full (success) Box. So the return value of findByID will be Full if everything succeeds, or Failure (with the given error message) otherwise. If you want the Failures to chain, then use ?~! instead.

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