2009-12-01 46 views
4

是否可以從Option中編寫一個廣義的orElse方法,該方法需要可變數目的參數?也就是說,不是,:如何編寫「orElse」的懶惰,可變參數版本

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) //, ... 

回答

4

按照The Scala Language Specification(4.6函數的聲明和定義)不能定義可變參數的名稱參數:

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

我試過這在repl,但不幸的是它似乎強制所有三個參數。 – 2009-12-01 16:24:54

+1

你是對的隱式類型轉換迫使評估。我修復了代碼。 – 2009-12-01 16:58:38

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,確保身體被評爲最多一次。所以與斯卡拉斯,它可以實現爲

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) //, ...