Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to reference a function that takes varargs

If I define the following function to return a function:

def foo(): (Int*) => String = { is =>
    is.map(_.toString).mkString(", ")
}

And then try to reference it:

val bar = foo()
bar(1, 2, 3)

I get the compiler error

Too many arguments (3) for method apply...

But when I explicitly define the reference type it compiles fine:

val bar2: (Int*) => String = foo()
bar2(4, 5, 6)

Is there any way I can define my function foo() without needing this explicit reference type?

like image 321
user79074 Avatar asked Jul 08 '19 10:07

user79074


People also ask

What is the rule for using Varargs?

Rules for varargs:There can be only one variable argument in the method. Variable argument (varargs) must be the last argument.

How do you call Varargs?

Syntax of VarargsA variable-length argument is specified by three periods or dots(…). This syntax tells the compiler that fun( ) can be called with zero or more arguments. As a result, here, a is implicitly declared as an array of type int[].

What symbol is used for a Varargs method parameter A B C --?

(three dots) is used in the formal parameter of a method. A method that takes variable number of arguments is called a variable-arity method, or simply a varargs method.


1 Answers

This is a known bug, which was "fixed" in Scala 2.13 by removing the ability to use * in types outside of method signatures at all.

If you only care about pre-2.13 Scala versions, you can use the workaround you've identified—explicitly annotate the function variable with the starred type. If you need to support 2.13, you can do something like this, thanks to Scala's single abstract method syntax:

trait MyVarargsFunc[-A, +B] {
  def apply(is: A*): B
}

val f: MyVarargsFunc[Int, String] = is => is.map(_.toString).mkString(", ")

Or if you want to get really fancy:

trait *=>[-A, +B] { def apply(is: A*): B }

val f: Int *=> String = is => is.map(_.toString).mkString(", ")

And then:

scala> f(1, 2, 3)
res0: String = 1, 2, 3

This will also work on 2.12 (I checked), and it should work on 2.11 with -Xexperimental (and it'll work even on 2.10 or vanilla 2.11 if you instantiate MyVarargsFunc explicitly—you just don't get the nice function literal syntax).

like image 174
Travis Brown Avatar answered Sep 22 '22 20:09

Travis Brown