说,我有一堆“验证”函数,如果没有错误则返回None,否则返回Some(String)指定错误消息。像下面这样......
def validate1:Option[String]
def validate2:Option[String]
def validate3:Option[String]
我将按顺序调用它们,一旦返回Some(String),我就会停止并返回相同的内容。如果它返回None,我会转到下一个,直到序列结束。如果所有这些都返回None,则返回None。
我想把它们粘在一起“for expression”。有点像...
for( a <- validate1; b <- validate2; c <- validate3) yield None;
然而,Option正好与我想要的完全相反。它在None处停止,并以Some(String)跟随。
我怎样才能实现这样的目标?
答案 0 :(得分:17)
您可以在选项
上使用或Else 方法将调用链接在一起validate1 orElse validate2 orElse validate3
或者您可以对转换为函数的验证方法集合进行折叠
val vlist= List(validate1 _, validate2 _, validate3 _)
vlist.foldLeft(None: Option[String]) {(a, b) => if (a == None) b() else a}
答案 1 :(得分:3)
scalaz库有一个名为Validation
的类型,它允许一些令人难以置信的体操,同时构建错误和成功。例如,假设您有一些方法可以返回失败消息或某些成功结果(A / B / C):
import scalaz._; import Scalaz._
def fooA : ValidationNEL[String, A]
def fooB : ValidationNEL[String, B]
def fooC : ValidationNEL[String, C]
这些可以与applicative functor一起使用来将调用链接在一起:
(foo1 <|**|> (foo2, foo3)) match {
case Success( (a, b, c) ) => //woot
case Failure(msgs) => //erk
}
请注意,如果foo1/2/3
中的任何一个失败,则整个合成将失败并显示失败消息的非空列表(NEL)。如果多个失败,则会收到所有失败消息。
这是一款杀手锏。 tor如何返回成功和失败的示例如下
def foo1 : ValidationNEL[String, Int] = 1.success
def foo2 : ValidationNEL[String, Double] = "some error msg".failNel
答案 2 :(得分:2)
你不能只将迭代器组合在一起然后取第一个元素吗?类似的东西:
scala> def validate1: Option[String] = {println("1"); None}
scala> def validate2: Option[String] = {println("2"); Some("error")}
scala> def validate3: Option[String] = {println("3"); None}
scala> (validate1.iterator ++ validate2.iterator ++ validate3.iterator).next
1
2
res5: String = error
答案 3 :(得分:0)
我认为您可能会受益于使用Lift的Box,其中Full
(即Some
),Empty
(即None
)和{{1} (Failure
有一个原因,它是空的,可以链接)。 David Pollak有good blog post介绍它。简而言之,您可能会做这样的事情(未经测试):
Empty
这并不比原始示例短,但在我看来,它更合乎逻辑,在def validate1: Box[String]
def validate2: Box[String]
def validate3: Box[String]
val validation = for (
validation1 <- validate1 ?~ "error message 1"
validation2 <- validate2 ?~ "error message 2"
validation3 <- validate3 ?~ "error message 3"
) yield "overall success message"
中验证成功,Full
验证失败。
然而,我们可以变小。首先,由于我们的验证函数返回Failure
,它们可以自己返回Box[String]
,我们不需要自己将Failure
转换为Empty
:
Failure
但是,val validation = for (
validation1 <- validate1
validation2 <- validate2
validation3 <- validate3
) yield "overall success message"
也有一个Box
方法,如果它是or
则返回相同的Box
,如果不是Full
则返回Box
。这会给我们:
val validation = validate1或validate2或validate3
但是,该行在第一次验证成功时停止,而不是第一次失败。制作另一种能够做你想要的方法(也许叫做unless
?)可能是有意义的,虽然我不能说它比理解方法真的有用得多。
然而,这里有一个小图书馆拉皮条:
scala> class Unless[T](a: Box[T]) {
| def unless(b: Box[T]) = {
| if (a.isEmpty) { a }
| else b
| }
| }
defined class Unless
scala> implicit def b2U[T](b: Box[T]): Unless[T] = new Unless(b)
b2U: [T](b: net.liftweb.common.Box[T])Unless[T]
scala> val a = Full("yes")
a: net.liftweb.common.Full[java.lang.String] = Full(yes)
scala> val b = Failure("no")
b: net.liftweb.common.Failure = Failure(no,Empty,Empty)
scala> val c = Full("yes2")
c: net.liftweb.common.Full[java.lang.String] = Full(yes2)
scala> a unless b
res1: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless b unless c
res2: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless c unless b
res3: net.liftweb.common.Box[java.lang.String] = Failure(no,Empty,Empty)
scala> a unless c
res4: net.liftweb.common.Box[java.lang.String] = Full(yes2)
根据我对Scala类型系统的有限理解,这是一个快速入侵,正如您在以下错误中看到的那样:
scala> b unless a
<console>:13: error: type mismatch;
found : net.liftweb.common.Full[java.lang.String]
required: net.liftweb.common.Box[T]
b unless a
^
然而,这应该足以让你走上正轨。
当然Lift ScalaDocs有关于Box的更多信息。