I posted the following as a bug and it was rejected as "expected behavior". A debate ensued on the bug list, one that belongs here rather than there, as Lukas wisely pointed out. So I'm posting it here.
Here's the issue. This smacks of leaky implementation details. Note that the example below doesn't actually pass anything for the varargs. Their presence in the definition of the class, though, impacts the behavior (and the error message seems to contradict itself. That is, the error message says if you use varargs with named parameters, you must specify the vararg exactly once. Well, except that you don't have to, as long as you specify the named parameters in declaration order.
-------
The order of named parameters passed affects behavior in an unexpected way.
class Family(val mom: String, val dad: String, val kids:String*) // this works val f1 = new Family(mom="Mom", dad="Dad") // this does not val f2 = new Family(dad="Dad", mom="Mom")
In summary, parameters passed in same order as class definition work fine. Parameters passed in a different order fail with complaint:
error: when using named arguments, the vararg parameter has to be specified exactly once
Only causes problem when also declaring a variable argument list.
-------
And furthermore (not in my original bug report), if you specify the vararg once but have mispositioned the parameters, you get a different error. Note the following: // this works val f3 = new Family(mom="Mom", dad="Dad", kids="Sammy", "Bobby") // this doesn't val f4 = new Family(dad="Dad", mom="Mom", kids="Sammy", "Bobby")
The error you get on f4 is: "error: positional after named argument."
while the error messages seem to be correct, the current behavior has led to complaints that the rules are too strict, and I certainly agree with that point.
I remember that there have been some efforts to make the compiler behaving more intelligently, but is has been shown that coming up with a solution which is a) easy to specify, complete and unambiguous AND b) easy to implement and test AND c) easy to understand AND d) easy to use is surprisingly hard.
As far as I know, that's the reason why the spec/compiler is pretty conservative and rejects code which looks straightforward to the human eye.
I think I remember that there were some commits regarding named parameters a few months ago. Did you try your code examples with Scala 2.10? Maybe there are some improvements already available?
No one had a chance to comment with a link to an explanation, but I believe
Simon.
An application that uses out-of-order params (oops) with varargs must look
like:
f(b=2,a=1,cs=Nil:_*)
f(b=2,a=1,cs=Seq(3,4):_*)
or
f(cs=Seq(3,4):_*,a=1,b=2) // which looks more interesting for some reason
Each param needs a positional arg, named arg or default arg; and repeated
params can't have a default.
Missing repeated params can't be taken to default to Nil:_* because of
overloading, presumably.
There's an old issue about an interaction between varargs and overloading
that just got some attention:
https://issues.scala-lang.org/browse/SI-2991 which indicates how persistent the cornery use cases are.
Maybe I'll wait until morning to explain this one to my dog, who is always
willing to listen:
scala> object F {
| def f(i: Int, ss: String*) = i+1
| def f(i: Int, j: Int = 5) = i+j
| }
defined module F
scala> F.f(1)
res9: Int = 2
scala> F.f(i=1)
<console>:9: error: ambiguous reference to overloaded definition,
both method f in object F of type (i: Int, j: Int)Int
and method f in object F of type (i: Int, ss: String*)Int
match argument types (i: Int)
F.f(i=1)
^
(Oh yeah, in the first case, prefer the method without default args; in the
second case? Wait, wait, don't tell me.)
Here is a use case:
scala> object K {
| def f(i: Int, j: Int, ss: String*) = i+1
| def f(i: Int, j: Int, k: Int = 5) = i+j+k
| }
defined module K
scala> K.f(j=1,i=2) // oops disambiguates
res18: Int = 8
scala> K.f(i=1,j=2)
<console>:9: error: ambiguous reference to overloaded definition,
both method f in object K of type (i: Int, j: Int, k: Int)Int
and method f in object K of type (i: Int, j: Int, ss: String*)Int
match argument types (i: Int,j: Int)
K.f(i=1,j=2)
^
On Thu, Nov 15, 2012 at 7:04 PM, Dianne Marsh <dmm...@gmail.com> wrote:
> Here's the issue. This smacks of leaky implementation details.
No argument there, but sometimes that happens and there's still no clearly
superior alternative. Which I'm not sure is the case this time, but the
whole picture is pretty complicated.
I was more taken by the tangential comment in the relevant ticket that we
should implement varargs with an implicit conversion. Since I know too well
how many different places varargs in the compiler touches, this was
immediately conceptually appealing. Some special handling would still be
necessary, but it feels like there would be a lot less of it. I might take
a gander.