你可以寫一個簡單的功能,但它會記錄只內Option
第一缺席值(由於for-comprehension
順序性質):
def logEmpty[T](opt: Option[T], msgIfNone: String) = {
if (opt.isEmpty) println(msgIfNone) //or something like logger.warn
opt
}
用法:
for {
a <- logEmpty(aOption, "Sorry no a")
b <- logEmpty(bOption, "Sorry no b")
c <- logEmpty(cOption, "Sorry no c")
d <- logEmpty(dOption, "Sorry no d")
} yield {...process...}
DSL樣:
implicit class LogEmpty[T](opt: Option[T]) {
def reportEmpty(msg: String) = {
if (opt.isEmpty) println(msg)
opt
}
}
用法:
for {
a <- aOption reportEmpty "Sorry no a"
b <- bOption reportEmpty "Sorry no b"
c <- cOption reportEmpty "Sorry no c"
d <- dOption reportEmpty "Sorry no d"
} yield {a + b + c + d}
例子:
scala> for {
| a <- Some("a") reportEmpty "Sorry no a"
| b <- None reportEmpty "Sorry no b"
| c <- Some("c") reportEmpty "Sorry no c"
| d <- None reportEmpty "Sorry no d"
| } yield {a + b + c + d}
Sorry no b
res19: Option[String] = None
如果您需要報告更多 - 最好的辦法是使用Validation
從scalaz或catsValidated
,所以你對abscence消息會被表示爲無效狀態Validated
。您始終可以將Validated
轉換爲Option
。
解決方案:
import cats._
import cats.data.Validated
import cats.data.Validated._
import cats.implicits._
implicit class RichOption[T](opt: Option[T]) {
def validOr(msg: String) =
opt.map(Valid(_)).getOrElse(Invalid(msg)).toValidatedNel
}
例子:
val aOption = Some("a")
val bOption: Option[String] = None
val cOption: Option[String] = None
scala> aOption.validOr("no a") |+| bOption.validOr("no b") |+| cOption.validOr("no c")
res12: cats.data.Validated[cats.data.NonEmptyList[String],String] = Invalid(NonEmptyList(no b, no c))
scala> aOption.validateOr("no a") |+| aOption.validateOr("no a again")
res13: cats.data.Validated[cats.data.NonEmptyList[String],String] = Valid(aa)
我用|+|
操作假設串聯,但你可以使用應用性製造商(或只是zip
),以及爲了在實現其他操作選項的內容:
scala> (aOption.validOr("no a") |@| aOption.validOr("no a again")) map {_ + "!" + _}
res18: cats.data.Validated[cats.data.NonEmptyList[String],String] = Valid(a!a)
scala> (aOption.validOr("no a") |@| bOption.validOr("no b") |@| cOption.validOr("no c")) map {_ + _ + _}
res27: cats.data.Validated[cats.data.NonEmptyList[String],String] = Invalid(NonEmptyList(no b, no c))
這兩個c在這Xor
和Validated
是Scala的Either
的變化,但Xor
和Validated
之間的區別是,Xor
(和Either
)則多爲「快速失敗的」一元的方式通過(對內涵又名做表示法)對比Validated
正在使用應用方法(它允許|@|
和zip
)。 flatMap
被視爲順序運算符,|@|
/zip
被視爲並行運算符(不要與執行模型混淆 - 它與運算符的性質是正交的)。您可以閱讀更多貓的文檔:Validated,Xor。
只是提一提,這種做法將不能提取選項值。你必須再做一步:'val List(a,b,c,d)= options .map(_._ 1.get)',這不是很安全 - 因爲存在匹配異常的風險在運行時!!!)如果你不匹配提取器與'List'的大小。除了'List'對於異構數據來說不是一個好的結構--OP並沒有說所有的選項都是相同的類型(你在這裏假設) - 它可能是'aOption:Option [String]','bOption :Option [Int]'等 – dk14
輕微:嚴格地說當你直接使用日誌記錄時(沒有Writer monad這是不切實際的) - 它不再是函數式編程 - 沒有什麼不好,因爲FP不是記錄和調試的最佳方法,但我仍然不會稱之爲「功能性」,也許只是「scala」。 – dk14
'printlns'並不是最終解決方案的代表,只是一個佔位符示例。不,你沒有運行時異常的風險,因爲你已經檢查過這個條件,'Options'列表是不可變的。同樣爲了這個目的,「List」是否是同質的並不重要。它可以是一個List [(Option [Any],String)]'並且工作得很好。 'process'步驟不必從我的列表中提取它們。 –