2016-06-08 21 views
3

假设我有两个功能foo: Int => Option[Int]bar: Int => Option[Int],并利用它们来处理列表:如何编写用orElse返回Option的函数?

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

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

回答

1

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

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

Tags.FirstVal状态的文档:

类型标签选择一个scalaz.Semigroup实例选择第一个操作数追加。

可以使用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(_)) 

您也可以使|+|工作作为orElse如果重写隐Monoid实例Option

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)(_)) 
+0

看起来这个答案有点不对,因为你想要更多的习惯用法,但我会把它留在这里,因为我相信这是用基本的斯卡拉/猫来完成的。 – Kolmar

+0

答案是好的))这是我正在寻找。 – Michael

+0

@Michael我已经更新了一个方法来使'| + |'像'orElse'一样工作。 – Kolmar

6

据我所知,这样的事情是不是在标准库,但你可以使用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) 
+1

感谢。它是否可用于'猫'或任何其他3D派对库? – Michael

+1

Foo。打我一分钟左右。 *与*完全相同,而'foo'和'bar'的定义相同。幽灵般的!有一个+1 :) –