你可以写一个简单的功能,但它会记录只内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'步骤不必从我的列表中提取它们。 –