我们可以有一组名字参数函数吗?

时间:2015-10-02 23:43:27

标签: scala

在Scala中,我们有一个名字参数,我们可以写

def foo[T](f: => T):T = {
   f // invokes f    
}
// use as:
foo(println("hello"))

我现在想对一组方法做同样的事情,我想将它们用作:

def foo[T](f:Array[ => T]):T = {     // does not work
   f(0) // invokes f(0)              // does not work
}
foo(println("hi"), println("hello")) // does not work

有什么办法可以做我想要的吗?我想出的最好的是:

def foo[T](f:() => T *):T = {
   f(0)() // invokes f(0)    
}
// use as:
foo(() => println("hi"), () => println("hello"))

def foo[T](f:Array[() => T]):T = { 
   f(0)() // invokes f(0)    
}
// use as: 
foo(Array(() => println("hi"), () => println("hello")))
编辑:正如Seth Tisue在对this answer的评论中指出的那样,提议的SIP-24并不是很有用。

这会产生问题的一个例子是效用函数trycatch的以下代码:

type unitToT[T] = ()=>T
def trycatch[T](list:unitToT[T] *):T = list.size match {
  case i if i > 1 => 
    try list.head()
    catch { case t:Any => trycatch(list.tail: _*) }
  case 1 => list(0)()
  case _ => throw new Exception("call list must be non-empty")
}

此处trycatch采用类型()=>T的方法列表,并连续应用每个元素,直到成功或达到结束。

现在假设我有两种方法:

def getYahooRate(currencyA:String, currencyB:String):Double = ???

def getGoogleRate(currencyA:String, currencyB:String):Double = ???

currencyA的一个单位转换为currencyB并输出Double

我使用trycatch作为:

val usdEuroRate = trycatch(() => getYahooRate("USD", "EUR"), 
                           () => getGoogleRate("USD", "EUR"))

我更喜欢:

val usdEuroRate = trycatch(getYahooRate("USD", "EUR"), 
                           getGoogleRate("USD", "EUR")) // does not work

在上面的示例中,我希望仅在getGoogleRate("USD", "EUR")引发异常时才调用getYahooRate("USD", "EUR")。这不是SIP-24的预期行为。

2 个答案:

答案 0 :(得分:2)

从Scala 2.11.7开始,答案是否定的。但是,有SIP-24,因此在将来的某个版本中,您的f: => T*版本可能会有效。

答案 1 :(得分:2)

Here是一个解决方案,虽然与直接按名称相比有一些限制:

import scala.util.control.NonFatal


object Main extends App {
  implicit class Attempt[+A](f: => A) {
    def apply(): A = f
  }

  def tryCatch[T](attempts: Attempt[T]*): T = attempts.toList match {
    case a :: b :: rest =>
      try a()
      catch {
        case NonFatal(e) =>
          tryCatch(b :: rest: _*)
      }
    case a :: Nil =>
      a()
    case Nil => throw new Exception("call list must be non-empty")
  }

  def a = println("Hi")
  def b: Unit = sys.error("one")
  def c = println("bye")
  tryCatch(a, b, c)

  def d: Int = sys.error("two")
  def e = { println("here"); 45 }
  def f = println("not here")

  val result = tryCatch(d, e, f)

  println("Result is " + result)
}

限制是:

  1. 使用块作为参数不会起作用;只有该块的最后一个表达式将包含在Attempt
  2. 如果表达式为Nothing类型(例如,如果bd未注释),则不会插入Attempt的转换,因为{ {1}}是每种类型的子类型,包括Nothing。据推测,同样适用于Attempt类型的表达式。