2014-12-05 54 views
3

我有一些函數(f2..fn)需要一個A,並返回Option [A]。這很好地工作(假設f1:X => Option [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") ... 

我不能立即看到一個優雅,慣用的方式來做到這一點 - 我不能看到任何東西放在<。>職位。

+0

偉大的答案,我都喜歡。我認爲並且最接近我所要求的,隱式類對現有代碼的損害最小,而scalaz可能是最正確的方法。所以不知道哪一個可以接受。我將嘗試隱式類,看看如何解決這個問題。 – 2014-12-05 17:19:24

回答

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

你可以使用一個隱含的類:

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

這使您可以擴展(也即講)的原班而不實際創建一個新的子類。

2

我覺得andThen是你正在尋找這裏的方法,雖然它並不漂亮:

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