Skip to content

Unnamed parameters after named ones #18122

Closed
@prolativ

Description

@prolativ

Compiler version

3.3.2-RC1-bin-20230630-c629090-NIGHTLY and before

Minimized code

The following snippet contains multiple cases obtained by uncommenting one of the commented lines at a time.

object Test {
  def foo1(x: Int, y: Int, z: Int) = println((x, y, z))
  def foo2(x: Int = 0, y: Int, z: Int) = println((x, y, z))
  def bar1(x: Int, ys: Int*) = println((x, ys))
  def bar2(x: Int = 0, ys: Int*) = println((x, ys))

  def main(args: Array[String]) = {
    // foo1(1, y = 2, 3)
    // foo2(1, y = 2, 3)
    // foo1(y = 1, 2, x = 3)
    // foo2(y = 1, 2, x = 3)
    // foo1(y = 1, 2, z = 3)
    // foo2(y = 1, 2, z = 3)
    // foo1(y = 1, 2)
    // foo2(y = 1, 2)

    // bar1()
    // bar2()
    // bar1(1)
    // bar2(1)
    // bar1(x = 1)
    // bar2(x = 1)
    // bar1(ys = 1)
    // bar2(ys = 1)
    // bar1(1, 2)
    // bar2(1, 2)
    // bar1(1, ys = 2)
    // bar2(1, ys = 2)
    // bar1(x = 1, 2)
    // bar2(x = 1, 2)
    // bar1(x = 1, ys = 2)
    // bar2(x = 1, ys = 2)
    // bar1(ys = 1, x = 2)
    // bar2(ys = 1, x = 2)
    // bar1(1, 2, 3)
    // bar2(1, 2, 3)
    // bar1(1, ys = 2, 3)
    // bar2(1, ys = 2, 3)
    // bar1(x = 1, 2, 3)
    // bar2(x = 1, 2, 3)
    // bar1(x = 1, ys = 2, 3)
    // bar2(x = 1, ys = 2, 3)
    // bar1(x = 1, 2, ys = 3)
    // bar2(x = 1, 2, ys = 3)
    // bar1(ys = 1, 2, x = 3)
    // bar2(ys = 1, 2, x = 3)
  }
}

Output

code scala 3.nightly scala 2.13.11
foo1(1, y = 2, 3) (1,2,3) (1,2,3)
foo2(1, y = 2, 3) (1,2,3) (1,2,3)
foo1(y = 1, 2, x = 3) (3,1,2) positional after named
foo2(y = 1, 2, x = 3) (3,1,2) positional after named
foo1(y = 1, 2, z = 3) missing argument x positional after named
foo2(y = 1, 2, z = 3) too many arguments positional after named
foo1(y = 1, 2) missing argument x positional after named, missing arguments x, z
foo2(y = 1, 2) (0,1,2) positional after named, missing argument z
code scala 3.nightly scala 2.13.11
bar1() missing argument x missing arguments x, ys
bar2() (0,ArraySeq()) def bar2 not compiling; missing argument ys
bar1(1) (1,ArraySeq()) (1,List())
bar2(1) (1,ArraySeq()) def bar2 not compiling
bar1(x = 1) (1,ArraySeq()) (1,List())
bar2(x = 1) (1,ArraySeq()) def bar2 not compiling
bar1(ys = 1) missing argument x vararg required exactly once
bar2(ys = 1) (0,ArraySeq(1)) def bar2 not compiling; vararg required exactly once
bar1(1, 2) (1,ArraySeq(2)) (1,ArraySeq(2))
bar2(1, 2) (1,ArraySeq(2)) def bar2 not compiling
bar1(1, ys = 2) (1,ArraySeq(2)) (1,ArraySeq(2))
bar2(1, ys = 2) (1,ArraySeq(2)) def bar2 not compiling
bar1(x = 1, 2) (1,ArraySeq(2)) (1,ArraySeq(2))
bar2(x = 1, 2) (1,ArraySeq(2)) def bar2 not compiling
bar1(x = 1, ys = 2) (1,ArraySeq(2)) (1,ArraySeq(2))
bar2(x = 1, ys = 2) (1,ArraySeq(2)) def bar2 not compiling
bar1(ys = 1, x = 2) (2,ArraySeq(1)) (2,List(1))
bar2(ys = 1, x = 2) (2,ArraySeq(1)) def bar2 not compiling
bar1(1, 2, 3) (1,ArraySeq(2, 3)) (1,ArraySeq(2, 3))
bar2(1, 2, 3) (1,ArraySeq(2, 3)) def bar2 not compiling
bar1(1, ys = 2, 3) (1,ArraySeq(2, 3)) (1,ArraySeq(2, 3))
bar2(1, ys = 2, 3) (1,ArraySeq(2, 3)) def bar2 not compiling
bar1(x = 1, 2, 3) (1,ArraySeq(2, 3)) (1,ArraySeq(2, 3))
bar2(x = 1, 2, 3) (1,ArraySeq(2, 3)) def bar2 not compiling
bar1(x = 1, ys = 2, 3) (1,ArraySeq(2, 3)) (1,ArraySeq(2, 3))
bar2(x = 1, ys = 2, 3) (1,ArraySeq(2, 3)) def bar2 not compiling
bar1(x = 1, 2, ys = 3) (1,ArraySeq(3, 2)) ys already specified
bar2(x = 1, 2, ys = 3) (1,ArraySeq(3, 2)) def bar2 not compiling; ys already specified
bar1(ys = 2, 3) missing argument x positional after named
bar2(ys = 2, 3) (0,ArraySeq(2, 3)) def bar2 not compiling; positional after named
bar1(ys = 1, 2, x = 3) (3,ArraySeq(1, 2)) positional after named
bar2(ys = 1, 2, x = 3) (3,ArraySeq(1, 2)) def bar2 not compiling; positional after named

Abbreviated error messages explained:

  • positional after named - positional after named argument (scala 2)
  • too many arguments - too many arguments for method ...
  • missing argument x - missing argument for parameter x of method ... (scala 3), not enough arguments for method ... Unspecified value parameter x (scala 2)
  • def bar2 not compiling - a parameter section with a `*`-parameter is not allowed to have default arguments (scala 2)
  • vararg required exactly once - when using named arguments, the vararg parameter has to be specified exactly once

Expectation

  • IMO scala 2 is right to disallow using unnamed arguments after named ones - otherwise it's quite unclear whether such an argument fills the first missing parameter slot (which I would personally assume, given that such usage is not disallowed completely) or the slot following the previous named parameter (which seems to be the case in scala 3, taking e.g. the behaviour of foo1(y = 1, 2, x = 3) into account)
  • Given that a user knows the current behaviour of the compiler for separate named and unnamed arguments in scala 3, the behaviour for varargs might seem consistent in some way, but otherwise it's also rather surprising. My personal first guess about named vararg argument was that I should pass a sequence like ys = Seq(2, 3) instead of ys = 2, 3, but this doesn't work. Should any of these syntaxes be legal at all? Especially the results for bar2(ys = 2, 3) and bar2(x = 1, 2, ys = 3) seem very confusing to me
  • The error messages for foo1(y = 1, 2, z = 3), foo2(y = 1, 2, z = 3), foo1(y = 1, 2) and bar1(ys = 2, 3) should be improved - currently it's not clear if the fix would be to add a name to an unnamed argument or to add another (possibly unnamed) argument

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions