How to write a lazy, variable argument version of “orElse”

非 Y 不嫁゛ 提交于 2020-01-03 16:52:11

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!