【问题标题】:Option operators with access to Option container有权访问选项容器的选项运算符
【发布时间】:2014-12-05 15:16:43
【问题描述】:

我有一些函数 (f2..fn) 接受 A,并返回 Option[A]。这很好用(假设 f1:X => Option[A])来做例如

f1(x) flatMap f2 flatMap f3

现在我希望能够记录发生的事情,尤其是在引入 None 的地方。我希望能够插入一个函数,例如:

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") ...

我无法立即看到一种优雅、惯用的方式来做到这一点 - 我看不到任何可以放在 <.> 位置的东西。

【问题讨论】:

  • 很好的答案,我都喜欢。我认为 andThen 最接近我的要求,隐式类对现有代码的损害最小,而 scalaz 可能是最正确的方法。所以不知道该接受哪一个。我将尝试隐式类,看看效果如何。

标签: scala


【解决方案1】:

这是 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 Either 来做类似的事情,但它更痛苦,因为你需要到处使用.toRightProjection

我假装验证是带有Validation.FlatMap 导入的单子,即使它不是,但如果您的 f1、2、3 不需要排序,您也可以使用应用版本来收集多个错误。 http://eed3si9n.com/learning-scalaz/Validation.html

【讨论】:

    【解决方案2】:

    我认为andThen 是您在这里寻找的方法,虽然它不是那么漂亮:

    (f1[A] _ andThen log_none("f1 failed"))(x) flatMap (f2[A] _ andThen log_none("f2 failed")) ...
    

    【讨论】:

      【解决方案3】:

      您可以使用隐式类:

      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")
      

      这使您可以扩展(可以这么说)原始类,而无需实际创建新的子类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-19
        • 2016-07-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-23
        • 2011-03-13
        相关资源
        最近更新 更多