假设我想在Scala中编写以下逻辑
val xdir = System.getProperty("XDir") if (xdir == null) error("No XDir") // log the error and exit val ydir = System.getProperty("YDir") if (ydir == null) error("No YDir") if (!new File(xdir).isDirectory) error("XDir is not a directory") if (!new File(ydir).isDirectory) error("YDir is not a directory") if (!new File(xdir).exists) error("XDir does not exis") if (!new File(ydir).exists) error("YDir does not exist") ... (and so on)
在Scala中编写此验证链的最佳方法是什么?
答案 0 :(得分:4)
类似的东西:
val batch = for{
a <- safe(doA, "A failed") either
b <- safe(doB, "B failed") either
c <- safe(doC, "C failed") either
} yield(a,b,c)
batch fold( error(_), doSuccess(_) )
安全执行a,您猜对了,安全(尝试/捕获)操作失败(左结果)消息并返回Either RightProjection(允许您在穿过点的时候进行上述批量操作)失败错误消息)
class Catching[T](f: => T) {
def either(msg: String) = {
try { Right(f).right } catch { Left(msg).right }
}
}
def safe[T](f: => T) = new Catching(f)
如果要记录特定的错误类型,也可以向Catching类添加选项方法,以及记录日志。
请参阅Jason Zaugg关于right biasing Either和this thread的解决方案,以及关于该主题的scala辩论。目前还没有达成共识,但大多数scala“heavyies”似乎都赞成。
这种方法的一个限制是,如果您尝试将条件(如果a = b)添加到for {}块,则它将无法编译(因为默认的两种过滤方法都返回Option)。解决方法是实现过滤器和withFilter,返回Either,我还没有弄清楚/做什么(如果有人已经这样做,请发帖)
答案 1 :(得分:4)
这是一些有用的东西:
def sysValue(prop: String) = Option(System.getProperty(prop)) //returns Option[String]
def trySysValue(prop: String) = //returns Either[String, String]
sysValue(prop) map Right getOrElse Left("Absent property: " + prop)
然后你可以通过右投影
使用Either
的monadic组合
val batch = //batch is Either[String, (File, File)]
for {
x <- trySysValue("XDir")).right
xf <- dir(x).right
y <- trySysValue("YDir").right
yf <- dir(y).right
}
yield (xf, yf)
其中:
def dir(s: String) = { //returns Either[String, File]
val f = new File(s)
if (!f.exists()) Left("Does not exist: " + f)
else if (!f.isDir()) Left("Is not a directory: " + f)
else Right(f)
}
Either
的左侧将是错误消息。这种monadic组合快速失败。您可以使用 scalaz XDir
来实现累积所有失败的合成(例如,如果YDir
和Validation
都不存在,您会看到两条消息)。在这种情况下,代码看起来像这样:
def trySysValue(prop: String) = //returns Validation[String, String]
sysValue(prop) map Success getOrElse ("Absent property: " + prop).fail
def dir(s: String) = {
val f = new File(s)
if (!f.exists())("Does not exist: " + f).fail
else if (!f.isDir()) ("Is not a directory: " + f).fail
else f.success
}
val batch = //batch is ValidationNEL[String, (File, File)]
(trySysValue("XDir")) flatMap dir).liftFailNel <|*|> (trySysValue("YDir")) flatMap dir).liftFailNel
答案 2 :(得分:1)
是的,您可以在没有scalaz的情况下使用验证,请参阅此处了解自我实现: http://applicative-errors-scala.googlecode.com/svn/artifacts/0.6/chunk-xhtml/apa.html HTH