varargs and named parameters ...

222 views
Skip to first unread message

Dianne Marsh

unread,
Nov 15, 2012, 10:04:01 PM11/15/12
to scala-...@googlegroups.com
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."



Opinions, please.

Thanks!
Dianne

Simon Ochsenreither

unread,
Nov 15, 2012, 10:47:26 PM11/15/12
to scala-...@googlegroups.com
Hi Dianne,

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?


Bye,

Simon

Som Snytt

unread,
Nov 16, 2012, 3:50:53 AM11/16/12
to Dianne Marsh, scala-...@googlegroups.com
On Thu, Nov 15, 2012 at 7:04 PM, Dianne Marsh <dmm...@gmail.com> wrote:
I posted the following as a bug and it was rejected as "expected behavior".

https://issues.scala-lang.org/browse/SI-6658

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

scala> K.f(1,2)
res20: Int = 2

Paul Phillips

unread,
Nov 16, 2012, 10:35:50 AM11/16/12
to Dianne Marsh, scala-...@googlegroups.com

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.
Reply all
Reply to author
Forward
0 new messages