【问题标题】:Method Dependencies and Error Handling方法依赖和错误处理
【发布时间】:2010-11-18 01:38:09
【问题描述】:

我正在 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]。

【问题讨论】:

    标签: scala lift


    【解决方案1】:

    对不起,我来晚了,但正如丹尼尔所说,你确实可以使用 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(带有给定的错误消息)。如果您希望将失败链接起来,请使用 ?~!而是。

    【讨论】:

    • 嘿 Jorge 看起来这会很有效,只是这是一个 sn-p,所以据我所知返回类型必须是 :NodeSeq。如果没有,我会收到一条错误消息,指出找不到该方法。为了解决这个问题,我可以将整个 for() 包装在一个块中并在其上调用 openOr(Text("fail")) 但是现在当其中一个依赖项失败时,我会收到消息“失败”。我试图使用 run() 方法,但我不太明白。有什么想法吗?
    • 太好了,谢谢豪尔赫。那就是“为什么我没有想到那些案例”之一。再次感谢
    【解决方案2】:

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

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

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

    这不会让您收到错误消息。对于那些,我猜我提到的方法,或者像~&gt; 这样的其他方法,可能会提供答案。

    【讨论】:

    • 谢谢丹尼尔,我试图以错误的方式使用 for 表达式来处理这个问题,我还不完全理解它们。回家后我会尝试一下,看看 Box 错误处理方法是否返回有关失败的具体信息。
    【解决方案3】:

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

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

    1. 检查条件
    2. 如果成功则继续
    3. 以其他方式终止并返回某些内容

    代码一旦遇到 None 就会发生一种短路,因此当动作序列返回时,返回值将被保留。要使用,以 Option 开头,如果 Option 是 Some,则写“ifSome”,如果 Option 是 None,则写“ifNone”,继续直到序列完成。如果在序列中的任何一点遇到 None ,则保留从“isNone”的按名称调用参数返回的选项,并在调用最终的“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)
      }
    }
    

    【讨论】:

    • 谢谢 Walter,这是一个有趣的解决方案,我回家后会尝试一下,锻炼我的 Scala 大脑肌肉。
    猜你喜欢
    • 1970-01-01
    • 2016-04-18
    • 1970-01-01
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多