Search code examples
scalaconstructoroperator-precedencedefault-parametersdefault-arguments

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


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?


Solution

  • 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.