我有这样的功能:
def foo(item: Item) : Option[Int] = Try{
// Some code that can blow up
}.toOption
我有一个项目列表,我想通过它们进行映射,并应用上述功能。但是,如果上面的函数崩溃并返回None,则映射结果应该是错误的:
items.map{
item => foo(item)
}
在这里做地图不正确吗?看起来不像]
这称为traverse
。如果可以使用cats,则非常简单:
import cats.implicits._
val result = items.traverse(foo) // Option[List[Int]]
如果没有,您可以很容易地实现它:
def traverse[A, B](data: List[A])(f: A => Option[B]): Option[List[B]] = {
@annotation.tailrec
def loop(remaining: List[A], acc: List[B]): Option[List[B]] =
remaining match {
case a :: as => f(a) match {
case Some(b) => loop(remaining = as, b :: acc)
case None => None
}
case Nil => Some(acc.reverse)
}
loop(remaining = data, acc = List.empty)
}
您可以这样使用:
val result = traverse(items)(foo) // Option[List[Int]]
(但是,我建议您使用cats,因为它更通用)。
对于开箱即用的短路,请考虑像这样用Try
包装列表映射
def fooUnsafe(item: Item): Int = // might throw
Try(items.map(fooUnsafe))
如果您希望保留def foo(item: Item) : Option[Int]
签名,则以下内容也会短路
Try(list.map(v => foo(v).get))