方法依赖性和错误处理

时间:2009-08-09 08:31:18

标签: scala lift

我正在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")
        }
}

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

3 个答案:

答案 0 :(得分:4)

对不起我这个节目已经很晚了,但丹尼尔说你确实可以使用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(带有给定的错误消息)。如果你想将故障链接起来,那么使用?〜!代替。

答案 1 :(得分:1)

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

现在,Box支持地图,flatMap,过滤器和foreach,因此它可以完全用于理解内容:

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

这不会得到错误消息。对于那些人,我猜测我提到的方法,或其他像~>这样的方法,可能会提供答案。

答案 2 :(得分:1)

我不知道Lift,所以我不能回答任何特定于Lift的问题。但是,我确实提出了一种方法来解决您的一个问题,即如何编写一系列检查然后操作操作而不需要使用嵌套模式匹配。

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

  1. 检查条件
  2. 如果成功则继续
  3. 终止并返回其他内容
  4. 一旦遇到None,代码会发生一种短路,因此当动作序列返回时,返回值将被保留。要使用,从Option开始,如果Option是Some,则写入“ifSome”,如果Option是None,则写入“ifNone”,继续直到序列结束。如果在序列中的任何一点遇到None,则会保留从“isNone”的call-by-name参数返回的Option,并在调用最终的“toOption”时返回。使用“toOption”返回实际的Option结果。

    在某些用例中查看“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)
      }
    }