How should Scala default arguments to refer to a previous positional argument?
Asked Answered
N

2

15

Scala-lang reference 5.5.1 and 6.6.1 gave me the impression that a default parameter would be able to refer to a previously evaluated one:

class Test(val first: String, val second: String = first)

but from experimenting it seems the only way to do this is to use the form:

class Test(val first: String)(val second: String = first)

and then define an auxiliary constructor or a creational companion class to avoid specifying the second set of brackets when creating. I don't really understand how this second constructor works, it looks like a curried function so I might guess that it is necessary to evaluate first independently of second, is this correct? Is this form necessary or is there some syntatic sugar I can use to tweak the first constructor into doing what I want?

Naevus answered 12/11, 2012 at 11:17 Comment(2)
There is no (sub)section 5.5.1 in the current Scala Language Specification. Did you mean 5.1.1 ("Constructor Invocations")?Daughter
Yes should have been 5.1.1. As @Travis Brown pointed out section 5.3 defining the scoping of the parameters as they are being evaluated would explain the compilation error.Naevus
D
10

As Travis Brown points out, you can indeed only refer to a previous argument in a default expression when it is from a previous argument list (so you do need to currify).

Now, regarding your particular use case, default arguments and method overloading are sometimes two ways of achieving the same thing.

I think the simplest solution to your scenario is simply to define Test as follows:

class Test(val first : String, val second : String) {
  def this(f : String) = this(f, f)
}

If you want to make it more complicated, an alternative way, using a companion object:

class Test(val first : String)(val second : String = first)
object Test {
  def apply(f : String) = new Test(f)
  def apply(f : String, s : String) = new Test(f)(s)
}

(A small difference is that now you create objects without new.)

What you cannot do, is define it as:

class Test(val first : String)(val second : String = first) {
  def this(f : String, s : String) = this(f)(s)
}

...because the curried version gets translated into (among other things) a method with the same signature as the overloaded contructor.

Daughter answered 12/11, 2012 at 11:32 Comment(0)
F
7

From 5.3 of the spec:

The scope of a formal value parameter includes all subsequent parameter sections and the template t.

Regular methods are the same, by the way (from 4.6):

The scope of a formal value parameter name x comprises all subsequent parameter clauses, as well as the method return type and the function body, if they are given.

I.e., whether you've got a constructor or an ordinary method, a value parameter name isn't in scope in its own parameter clause. In your second version the constructor has two parameter clauses, and first is only in scope in the second. See 5.3 for more detail about multiple parameter clauses.

Follow answered 12/11, 2012 at 11:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.