问题
Is it possible to write a generalised orElse
method from Option
that takes a variable number of arguments? That is, instead of:
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 ...
You could use:
orElse(o1, o2, o3) //, ...
回答1:
According to the The Scala Language Specification (4.6 Function Declarations and Definitions) you cannot define varargs by-name parameters:
ParamType ::= Type
| ‘=>’ Type
| Type ‘*’
scala> def orElse(x : (=> String)*)
<console>:1: error: no by-name parameter type allowed here
def orElse(x : (=> String)*)
You could replace the lazy arg with function and an implicit type conversion:
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)
回答2:
I found the question a bit late :). One possibility is to wrap => A
into a helper class together with a helper function to simplify its creation:
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);
The extractor isn't required, it just allows easy matching on the helper. Then we can write
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) //, ...
This is just a simplified solution, a more realistic one would be
def orElse[A](x: Option[A], xs: Helper[Option[A]]*): Option[A]
with a more efficient implementation.
There already is a class similar to Helper
in Scalaz, called Name
with implementation Need
that ensures that the body is evaluated at most once. So with Scalaz, it could be implemented as
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) //, ...
来源:https://stackoverflow.com/questions/1826145/how-to-write-a-lazy-variable-argument-version-of-orelse