我有一些函数(f2..fn)取一个A,然后返回选项[A]。这很好地工作(假设f1:X =>选项[A])来做例如。
f1(x) flatMap f2 flatMap f3
现在我希望能够记录发生的事情,特别是在无介绍的地方。我希望能够插入一个函数,如:
log_none(m:String):Option[A] => Option[A]
如果遇到None,则会产生记录的副作用。
选项功能似乎都不适用于此(阅读后如tonymorris.github.io/blog/posts/scalaoption-cheat-sheet /)
理想情况下,它看起来像:
f1(x) <.> log_none("f1 failed") flatMap f2 <.> log_none("f2 failed") ...
我无法立即看到一种优雅,惯用的方法 - 我无法在&lt;。&gt;中看到任何内容。位置。
答案 0 :(得分:4)
对于scalaz的验证,这是一个很好的例子。它类似于Option但不是None,它会给你一个错误值。
在:
def f1(x: Int): Option[Int]
def f2(x: Int): Option[Int]
def f2(x: Int): Option[Int]
for {
x1 <- f1(x)
x2 <- f2(x1)
x3 <- f3(x2)
} yield x3
您可以使用toSuccess隐式
进行简单转换import scalaz.{Validation, Success, Failure}
import scalaz.Validation.FlatMap._
import scalaz.syntax.std.option._
def oldf1(x: Int): Option[Int]
def f1(x: Int): Validation[String, Int] = oldf1(x).toSuccess("f1 failed")
def f2(x: Int): Validation[String, Int]
def f2(x: Int): Validation[String, Int]
val validatedX3: Validation[String, Int] = for {
x1 <- f1(x)
x2 <- f2(x1)
x3 <- f3(x2)
} yield x3
validatedX3 match {
case Success(i) =>
Some(i)
case Failure(errStr) =>
log(errStr)
None
}
或者替代
validatedX3.leftMap(log).toOption
你可以使用scala进行类似的事情,但它更痛苦,因为你需要在整个地方使用.toRightProjection
。
我假装验证与Validation.FlatMap
导入是一致的,即使它不是,但是如果您的f1,2,3不是,那么您也可以使用应用版本来收集多个错误。需要排序。 http://eed3si9n.com/learning-scalaz/Validation.html
答案 1 :(得分:2)
我认为andThen
是您在这里寻找的方法,虽然它并不那么漂亮:
(f1[A] _ andThen log_none("f1 failed"))(x) flatMap (f2[A] _ andThen log_none("f2 failed")) ...
答案 2 :(得分:1)
您可以使用隐式类:
scala> implicit class LogEmptyOption[A](opt: Option[A]) {
| def logNone(m: String): Option[A] = {
| if (opt.isEmpty)
| println(m)
| opt
| }
| }
defined class LogEmptyOption
scala> Option.empty[String].logNone("No Such element")
这使您可以扩展(可以说)原始类,而无需实际创建新的子类。