如何使用 orElse 组合返回 Option 的函数?

How to compose functions returning Option with orElse?

假设我有两个函数 foo: Int => Option[Int]bar: Int => Option[Int] 并使用它们来处理列表:

val xs: List[Int] = ...
xs flatMap {x => foo(x) orElse bar(x)}

我可以写 xs flatMap (foo orElse bar) 而不是 {x => foo(x) orElse bar(x)} 吗?
我可以使用 3d 派对库。

据我所知,标准库中没有这样的东西,但您可以使用 implicit class:

自己添加
implicit class WithOrElse[T, R](f: T => Option[R]) {
  def orElse(g: T => Option[R])(x: T) = f(x) orElse g(x)
}

示例:

val foo: Int => Option[Int] = x => if (x % 2 == 0) Some(x) else None
val bar: Int => Option[Int] = x => if (x % 3 == 0) Some(x) else None

val xs = List(1, 2, 3, 4, 5, 6, 7, 8)

xs flatMap (foo orElse bar)

您可以使用来自 scalaz 的 Semigroup 类型类的组合操作 (|+|)。

Option 的默认半群使用内容的半群,如果两个值都存在,则合并这些值。 因此,要模仿 orElse 行为,您需要将 Option 包装在 Tags.FirstVal 标记中。

Tags.FirstVal 的文档指出:

Type tag to choose a scalaz.Semigroup instance that selects the first operand to append.

您可以使用 Tag.subst.unsubst 方法从某些 F[T] 中包装和解包类型 T。在我看来,您还必须在类型推断方面帮助 Scala。

总而言之,组合函数如下所示:

type F[T] = Int => Option[T]
val f = Tags.FirstVal.unsubst(
  Tags.FirstVal.subst[F, Int](foo) |+| 
  Tags.FirstVal.subst[F, Int](bar))

要将它与 flatMap 一起使用,您必须以某种方式使用从 OptionList 的隐式转换。所以 flatMap 调用可能如下所示:

xs flatMap (f(_))

如果覆盖 Option 的隐式 Monoid 实例,您还可以使 |+|orElse 一样工作:

import scalaz._, Scalaz._
implicit val inst: Monoid[Option[Int]] = 
  Tags.First.unsubst(scalaz.std.option.optionFirst[Int])

val foo: Int => Option[Int] = x => if (x % 2 == 0) Some(x) else None
val bar: Int => Option[Int] = x => if (x % 3 == 0) Some(x) else None
val xs = List(1, 2, 3, 4, 5, 6, 7, 8)

xs.flatMap((foo |+| bar)(_))