Зависимости методов и обработка ошибок

StackOverflow https://stackoverflow.com/questions/1250957

  •  12-09-2019
  •  | 
  •  

Вопрос

Я создаю небольшую систему управления проблемами в Lift, чтобы изучить Scala и Lift.

У меня есть представление, в котором отображается одна проблема, принадлежащая проекту.Прежде чем привязать данные к шаблону представления, я хочу убедиться, что у меня есть все необходимые данные, поэтому я хочу специально проверить следующее:

  • Был предоставлен параметр идентификатора проекта.
  • Проект существует с указанным идентификатором проекта.
  • Был предоставлен параметр идентификатора проблемы.
  • Существует проблема с указанным идентификатором проблемы.

И их необходимо оценивать по порядку, поэтому, если бы я писал это сейчас с моим текущим пониманием 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].

Это было полезно?

Решение

Извините, что так опоздал на шоу, но, как говорит Дэниел, вы действительно можете использовать 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» (с данным сообщением об ошибке) в противном случае.Если вы хотите, чтобы сбои объединялись в цепочку, используйте ?~!вместо.

Другие советы

Я не знаю Lift, но есть пара вещей, которые я заметил в его реализации.Одним из них являются методы отказа: ?~ и ?~!.Я не уверен, как именно их можно использовать, но кажется, что это полезно.Другой open_!, чтобы генерировать исключения.

Теперь Box поддерживает карты, FlatMap, фильтры и foreach, поэтому их можно полностью использовать внутри для понимания:

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

Это не приведет к появлению сообщений об ошибках.Для них, я предполагаю, методы, которые я упомянул, или другие подобные ~>, может дать ответ.

Я не знаю Lift, поэтому не могу ответить ни на один вопрос, касающийся Lift.Однако я нашел способ решить одну из ваших проблем: как написать последовательность действий «проверить, затем выполнить», не прибегая к сопоставлению вложенных шаблонов.

Основной используемый здесь тип данных — Option, но я уверен, что его будет довольно легко адаптировать под ваши нужды.Здесь мы хотим выполнить последовательность

  1. проверить состояние
  2. продолжить в случае успеха
  3. завершить и вернуть что-то в противном случае

Код выполняет своего рода короткое замыкание, когда обнаруживает None, поэтому возвращаемое значение сохраняется при возврате последовательности действий.Чтобы использовать, начните с Option, затем напишите «ifSome», если Option — Some, и «ifNone», если Option — None, продолжайте до тех пор, пока последовательность не завершится.Если None встречается в любой точке последовательности, Option, возвращенный из параметра вызова по имени "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)
  }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top