Mixing varargs and by-name

I’ve been trying to do something which, when reduced, looks a little like this:

def lazyCount(is: (=> Int)*): Int =
  is.length

Aside from crashing the compiler when CC is enabled, this does not compile:

By-name parameter type => Int not allowed here.
  def lazyCount(is: (=> Int)*): Int =
                     ^^^^^^

I wonder if there’s a particular reason for that, especially given that we can relatively easily get the same feature with context functions:

trait Dummy
object Dummy:
  given Dummy {}

def lazyCount(is: (Dummy ?=> Int)*): Int =
  is.length

This compiles and exhibits the expected behaviour (well, expected by me at least):

// Only used to show that none of the arguments to lazyCount are evaluated.
def log(i: Int) =
  println(s"Creating $i")
  i

println(lazyCount(log(1), log(2), log(3)))
// 3

Your use case is mentioned here:

I’m reminded by the old ticket

that it was SIP-24 which FSR wound up in SIP limbo.

3 Likes

EDIT: sorry didnt realize you wanted a sequence of lazy values, as opposed to a lazy sequence…

Going by martin’s comments, this seems to work fine for me if I remove the parens in the def site arg type annotation. That crash also disappears.

def method(a: => A*): Unit = ()

class A:
  println("initialized A") // <- never gets called

method(A(), A())

println("asdf")
1 Like

I think this works because you’re ignoring a entirely. If you update your code so that method interacts with it, you’ll see that you will get things printed: Scastie - An interactive playground for Scala.

Which, to me, says that a: => A* is a by-name list of A, not a list of by-name As, although I might very well be wrong.

As the song says, “You may be wrong, but you may be right.”

1 Like