以惯用的方式组合选项

时间:2013-12-13 12:51:51

标签: scala functional-programming

我将在Scala中编写这个,但它更像是一个函数式编程问题。

我有

def foo(x: A): Option[B]

def bar(x:B, y:B): C

执行以下操作的最佳方式是什么:

def compose(x:A, y:A): Option[C]

如果foo(y)的foo(x)为None,则compose(x,y)为None,否则compose(x,y)为bar(foo(x).get,foo(y) 。得到)。我能想到的最好的是:

foo(a).flatMap( aRes => foo(b).map( bRes => bar(a,b)))

2 个答案:

答案 0 :(得分:5)

以下是您当前解决方案的语法糖:

def compose(x: A, y: A): Option[C] = for {
  fx <- foo(x)
  fy <- foo(y)
} yield bar(fx, fy)

有时候这种方法比写出flatMapmap更好,有时则不然。你可能会发现你很快就会对这种事情产生强烈的偏好。要么被认为是惯用的Scala。

但是,从功能编程的角度来看,你已经表明你对这个问题更感兴趣,但值得注意的是,上述解决方案在某种意义上是过度的。他们利用Option是monadic的事实,但是对于这个操作,你实际上并不需要所有这些功能 - Option具有应用程序仿函数实例的事实就足够了。为了非常非正式地总结,flatMap为您提供了此处不需要的排序,因为fy的计算不依赖于fx的计算。使用Option的applicative functor可以更清楚地捕获两个计算之间没有依赖关系的事实。

Scala标准库不提供任何类型的applicative functors表示,但Scalaz表示,并且使用Scalaz,你可以像这样编写你的方法(参见我的答案的“附录”{{3} }对于语法的一些讨论):

import scalaz._, Scalaz._

def compose(x: A, y: A): Option[C] = (foo(x) |@| foo(y))(bar)

这将产生与上述实现相同的结果,但使用更合适的抽象。

答案 1 :(得分:3)

怎么样:

for (x <- foo(a); y <- foo(b)) yield bar(x,y) 

例如:

type A = Int
type C = (A,A)
def foo(x: A): Option[A] = if (x > 0) Some(x) else None
def bar(x: A, y: A): C = x -> y 
for (x <- foo(1); y <- foo(2)) yield bar(x,y)
// Option[C] = Some((1,2))
for (x <- foo(-1); y <- foo(2)) yield bar(x,y)
// Option[C] = None

根据您的口味,第一个可以写成:

for { 
    x <- foo(a)
    y <- foo(b) 
} yield bar(x,y)