在 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
在上面的示例中,我希望仅在 getYahooRate("USD", "EUR")
时调用 getGoogleRate("USD", "EUR")
抛出异常。这不是 SIP-24 的预期行为。
最佳答案
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)
}
限制是:
- 使用 block 作为参数是行不通的;只有 block 的最后一个表达式将被包装在
Attempt
中。 - 如果表达式的类型为
Nothing
(例如,如果b
和d
未注释),则转换为未插入 Attempt
,因为Nothing
是每个类型的子类型,包括Attempt
。大概同样适用于Null
类型的表达式。
关于scala - 我们可以有一组按名称参数的函数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32917373/