2

私は、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] を返すことに注意してください。

4

3 に答える 3

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(指定されたエラーメッセージを含む)になります。失敗を連鎖させたい場合は、?〜!を使用します。代わりは。

于 2009-08-13T00:52:57.497 に答える
1

Lift についてはわかりませんが、Lift の実装で見たことがいくつかあります。それらの 1 つは失敗メソッドです:?~および?~!. それらをどのように使用するのか正確にはわかりませんが、便利なようです。もう 1 つはopen_!、例外をスローすることです。

現在、Box は map、flatMap、filter、および foreach をサポートしているため、理解のために完全に使用できます。

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

これにより、エラー メッセージが表示されなくなります。それらについては、私が言及した方法、または のような他の方法~>が答えを提供するかもしれないと推測しています。

于 2009-08-09T17:08:27.703 に答える
1

Lift について詳しくないので、Lift 固有の質問にはお答えできません。ただし、ネストされたパターン マッチングに頼らずに、チェックしてから操作する一連のアクションを記述する方法という問題の 1 つを解決する方法を思いつきました。

ここで使用されている主なデータ型は Option ですが、ニーズに合わせるのは非常に簡単であると確信しています。ここで達成したいことは、次のシーケンスを実行することです

  1. 状態確認
  2. 成功したら続行
  3. それ以外の場合は、終了して何かを返します

コードは、None に遭遇すると一種のショート サーキットを実行するため、一連のアクションが返されたときに戻り値が保持されます。使用するには、Option で開始し、Option が Some の場合は「ifSome」、Option が None の場合は「ifNone」と記述し、シーケンスが終了するまで続けます。シーケンスの任意の時点で None が検出された場合、"isNone" の call-by-name パラメータから返された Option は保持され、最後の "toOption" が呼び出されたときに返されます。実際のオプションの結果を取得するには、「toOption」を使用します。

いくつかの使用例については、「メイン」の例を確認してください。幸運を!

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)
  }
}
于 2009-08-09T17:19:39.863 に答える