Can we have an array of functions by parameter name?

In Scala, we have by-name parameters where we can write

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

Now I want to do the same with an array of methods, i.e. I want to use them as:

 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 

Is there a way to do what I want? The best I came up with is:

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

or

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

EDIT: The proposed SIP-24 is not very useful, as Seth Tisue pointed out in a comment on this answer .

An example where this will be problematic is the following trycatch utility function 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") } 

Here trycatch takes a list of methods of type ()=>T and sequentially applies each element until it succeeds or the end is reached.

Now suppose I have two methods:

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

and

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

which convert one unit currencyA to currencyB and output Double .

I use trycatch as:

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

I would prefer:

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

In the above example, I would like getGoogleRate("USD", "EUR") called only if getYahooRate("USD", "EUR") throws an exception. This is not the intended behavior of SIP-24.

+2
scala
02 Oct '15 at 23:43
source share
2 answers

As with Scala 2.11.7, the answer is no. However, there is SIP-24 , so in some future versions your version of f: => T* may be possible.

+2
Oct 02 '15 at 23:54
source share

Here is a solution, albeit with a few limitations compared to a direct call by name:

 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) } 

Limitations:

  • Using a block as an argument will not work; only the last block expression will be wrapped in Attempt .
  • If the expression is of type Nothing (for example, if b and d were not annotated), no conversion to Attempt is inserted, because Nothing is a subtype of each type, including Attempt . Supposedly the same applies to an expression of type Null .
+2
Nov 30 '16 at 3:46
source share



All Articles