7

What is wrong is the following method?

def someMethod(funcs: => Option[String]*) = {
 ...
}
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
Jonhnny Weslley
  • 1,080
  • 2
  • 13
  • 24
  • The name of the formal parameter, `funcs`, is highly suspect. By-name parameters, while implemented with a thunk, are not overtly functions. – Randall Schulz Apr 11 '10 at 22:42
  • Nor are they what's commonly referred to as lazily evaluated arguments. I.e., if you use the value multiple times, the expression given as the actual parameter is evaluated multiple times. That has performance consequences and if that expression has side-effects, they'll occur multiply, too. – Randall Schulz Apr 12 '10 at 04:05
  • That may be a solution: http://stackoverflow.com/a/34373967/2825964 – Markus Marvell Dec 19 '15 at 18:36

2 Answers2

5

That actually "works" under 2.7.7 if you add parens:

scala> def someMethod(funcs: => (Option[String]*)) = funcs
someMethod: (=> Option[String]*)Option[String]*

except it doesn't actually work at runtime:

scala> someMethod(Some("Fish"),None)
    scala.MatchError: Some(Fish)
at scala.runtime.ScalaRunTime$.boxArray(ScalaRunTime.scala:136)
at .someMethod(<console>:4)
at .<init>(<console>:6)
at .<clinit>(<console>) ...

In 2.8 it refuses to let you specify X* as the output of any function or by-name parameter, even though you can specify it as an input (this is r21230, post-Beta 1):

scala> var f: (Option[Int]*) => Int = _
f: (Option[Int]*) => Int = null

scala> var f: (Option[Int]*) => (Option[Int]*) = _
<console>:1: error: no * parameter type allowed here
       var f: (Option[Int]*) => (Option[Int]*) = _

But if you try to convert from a method, it works:

scala> def m(oi: Option[Int]*) = oi
m: (oi: Option[Int]*)Option[Int]*

scala> var f = (m _)
f: (Option[Int]*) => Option[Int]* = <function1>

scala> f(Some(1),None)
res0: Option[Int]* = WrappedArray(Some(1), None)

So it's not entirely consistent.

In any case, you can possibly achieve what you want by passing in an Array and then sending that array to something that takes repeated arguments:

scala> def aMethod(os: Option[String]*) { os.foreach(println) }
aMethod: (os: Option[String]*)Unit

scala> def someMethod(funcs: => Array[Option[String]]) { aMethod(funcs:_*) }
someMethod: (funcs: => Array[Option[String]])Unit

scala> someMethod(Array(Some("Hello"),Some("there"),None))
Some(Hello)
Some(there)
None

If you really want to (easily) pass a bunch of lazily evaluated arguments, then you need a little bit of infrastructure that as far as I know doesn't nicely exist in the library (this is code for 2.8; view it as inspiration for a similar strategy in 2.7):

class Lazy[+T](t: () => T, lt: Lazy[T]) {
  val params: List[() => T] = (if (lt eq null) Nil else t :: lt.params)
  def ~[S >: T](s: => S) = new Lazy[S](s _,this)
}
object Lz extends Lazy[Nothing](null,null) {
  implicit def lazy2params[T : Manifest](lz: Lazy[T]) = lz.params.reverse.toArray
}

Now you can easily create a bunch of parameters that are lazily evaluated:

scala> import Lz._  // To get implicit def
import Lz._

scala> def lazyAdder(ff: Array[()=>Int]) = {
     |   println("I'm adding now!");
     |   (0 /: ff){(n,f) => n+f()}
     | }
lazyAdder: (ff: Array[() => Int])Int

scala> def yelp = { println("You evaluated me!"); 5 }
yelp: Int

scala> val a = 3
a: Int = 3

scala> var b = 7
b: Int = 7

scala> lazyAdder( Lz ~ yelp ~ (a+b) )
I'm adding now!
You evaluated me!
res0: Int = 15

scala> val plist = Lz ~ yelp ~ (a+b)
plist: Lazy[Int] = Lazy@1ee1775

scala> b = 1
b: Int = 1

scala> lazyAdder(plist)
I'm adding now!
You evaluated me!
res1: Int = 9
Rex Kerr
  • 166,841
  • 26
  • 322
  • 407
3

Evidently repeated arguments are not available for by-name parameters.

Randall Schulz
  • 26,420
  • 4
  • 61
  • 81
  • But, just for kicks, try this: `def f(oi: Option[Int]*) = oi` in the REPL. Interesting, eh? – Rex Kerr Apr 12 '10 at 01:58
  • @Rex_Kerr: I guess. What interesting thing are you referring to? – Randall Schulz Apr 12 '10 at 03:44
  • 1
    You can return Option[Int]* from a method, and you can use (f _) to make it a function. But try to find syntax that allows you to represent the type. So it's unclear to me whether repeated arguments are not available for by-name parameters, or whether the syntax doesn't allow you to express the type that you want (or both). – Rex Kerr Apr 12 '10 at 05:31
  • 1
    Not true post Scala 2.9.x. Sample REPL session from current trunk: scala> def f(oi: Option[Int]*) = oi ... f: (oi: Option[Int]*)Seq[Option[Int]] – Miles Sabin Aug 23 '11 at 10:56