如何编写“orElse”的惰性,可变参数版本

时间:2009-12-01 13:19:29

标签: scala option variadic-functions lazy-evaluation

是否可以从orElse编写一个带有可变数量参数的通用Option方法?也就是说,而不是:

lazy val o1 = { println("foo"); None }
lazy val o2 = { println("bar"); Some("bar") }
lazy val o3 = { println("baz"); Some("baz") } 
// ...
o1 orElse o2 orElse o3 // orElse ...

您可以使用:

orElse(o1, o2, o3) //, ...

2 个答案:

答案 0 :(得分:4)

根据The Scala Language Specification(4.6函数声明和定义),您无法定义varargs按名称参数:

ParamType ::= Type
| ‘=>’ Type
| Type ‘*’

scala> def orElse(x : (=> String)*)
<console>:1: error: no by-name parameter type allowed here
       def orElse(x : (=> String)*)

您可以使用函数和隐式类型转换替换惰性arg:

def orElse[T](x : (()=> Option[T])*) : Option[T] = 
    if(x.isEmpty) None else x.first.apply.orElse(orElse((x drop 1) :_*))
implicit def anyToFun0[T](t : => T) : (() => T) = () => t
orElse(o1, o2, o3)

答案 1 :(得分:1)

我发现问题有点晚了:)。一种可能性是将=> A与辅助函数一起包装到辅助类中以简化其创建:

import scala.language.implicitConversions

class Helper[+A](value: => A) extends Function0[A] {
  override def apply(): A = value;
}
object Helper {
  def unapply[A](h: Helper[A]): Option[A] = Some(h());
}
implicit def toHelper[A](body: => A) = new Helper(body);

提取器不是必需的,它只是允许在助手上轻松匹配。然后我们可以写

def orElse[A](xs: Helper[Option[A]]*): Option[A] =
  xs.collectFirst[A]({
    case Helper(Some(r)) => r;
  })

lazy val o1 = { println("foo"); None }
lazy val o2 = { println("bar"); Some("bar") }
lazy val o3 = { println("baz"); Some("baz") }

orElse(o1, o2, o3) //, ...

这只是一个简化的解决方案,一个更现实的解决方案

def orElse[A](x: Option[A], xs: Helper[Option[A]]*): Option[A]

实施更高效。


在Scalaz中已经存在类似于Helper的类,名为Name,其实现为Need,可确保最多对主体进行一次评估。因此,对于Scalaz,它可以实现为

import scala.language.implicitConversions
import scalaz._
import scalaz.Scalaz._

implicit def toNeed[A](body: => A): Name[A] = Need(body);

def orElse[A](xs: Name[Option[A]]*): Option[A] =
  xs.collectFirst[A]({
    case Name(Some(r)) => r;
  })

lazy val o1 = { println("foo"); None }
lazy val o2 = { println("bar"); Some("bar") }
lazy val o3 = { println("baz"); Some("baz") }

orElse(o1, o2, o3) //, ...