Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Secondary constructors don't allow for defaults with the same type #7870

Closed
scabug opened this issue Sep 23, 2013 · 10 comments
Closed

Secondary constructors don't allow for defaults with the same type #7870

scabug opened this issue Sep 23, 2013 · 10 comments
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Sep 23, 2013

When I define secondary constructors with default parameters, if two parameters share the same type it gives me a "double definition" error. Below is the offending code:

case class Person(name: String = "", address: String = "") {
  def this(p: Person, name: String = null, address: String = null) = this(
    if(name == null) p.name else name,
    if(address == null) p.address else address
  )
}

Error:

Person.scala:2: error: double definition:
method <init>$default$2:=> String and
method <init>$default$2:=> String at line 1
have same type
  def this(p: Person, name: String = null, address: String = null) = this(
                      ^
one error found
@scabug
Copy link
Author

scabug commented Sep 23, 2013

Imported From: https://issues.scala-lang.org/browse/SI-7870?orig=1
Reporter: Maurício Szabo (mauricio.szabo)
Affected Versions: 2.11.0-M4
See #2325

@scabug
Copy link
Author

scabug commented Sep 23, 2013

@retronym said:
It should issue the same implementation restriction as:

scala> class C { def foo(a: Int = 0) = 0; def foo(a: Int = 0, b: Any) = 0 }
<console>:7: error: in class C, multiple overloaded alternatives of method foo define default arguments.
       class C { def foo(a: Int = 0) = 0; def foo(a: Int = 0, b: Any) = 0 }
             ^

@scabug
Copy link
Author

scabug commented Sep 23, 2013

@retronym said:
scala/scala#2979

@scabug
Copy link
Author

scabug commented Sep 23, 2013

Maurício Szabo (mauricio.szabo) said:
But it doesn't:

class Foo(a: Int = 0) { 
  def this(a: String = "0") = this(a.toInt)
}

Furthermore, these snippets compiles too:

class Foo(a: String = "0") { def this(p1: String, p2: Int = 0) = this(p1 + p2.toString) }

class Foo(val a: String = "0") { 
  def this(p1: String, p2: String = "0") = this(p1 + p2.toString)
}

In these two examples above, I have constructors with default parameters, and Scala can inflect which one I want (even in the second case, when it's not really clear if I do new Foo("A String") if it should call the primary or the secondary).

In the ticket's example, it's clear that the secondary constructor is not "ambiguous" nor "double defined", as the first parameter it expects is a mandatory one AND has no default parameter.

@scabug
Copy link
Author

scabug commented Sep 23, 2013

@retronym said:
We are not being as strict as intended. The check should be consistent with the checks for clashing default getters in regular methods, meaning that we should disallow that case.

The constructor itself is not double defined. It is the default getter. More information on the implementation of default arguments is available here: http://docs.scala-lang.org/sips/completed/named-and-default-arguments.html

@scabug
Copy link
Author

scabug commented Sep 23, 2013

Maurício Szabo (mauricio.szabo) said:
So, what's the expected behavior? Only one constructor can have default arguments, or is it possible to have multiple constructors with default arguments? If it's the second option, what are the rules of these secondary constructors with default arguments?

@scabug
Copy link
Author

scabug commented Sep 23, 2013

@retronym said (edited on Sep 23, 2013 5:00:23 PM UTC):
If you have overloads of f in a class, only one of them may use define defaults:

scala> class C { def foo(a: Int = 0, b: Int = 0) = 0; def foo(a: String = "") = 0 }
<console>:7: error: in class C, multiple overloaded alternatives of method foo define default arguments.
       class C { def foo(a: Int = 0, b: Int = 0) = 0; def foo(a: String = "") = 0 }
             ^

This check wasn't working for constructors. My change brings them under the same rules as regular methods.

scala> class C { def this(a: Int = 0, b: Int = 0) = this(); def this(a: String = "") = this() }
<console>:7: error: in class C, multiple overloaded alternatives of constructor C define default arguments.
       class C { def this(a: Int = 0, b: Int = 0) = this(); def this(a: String = "") = this() }
             ^

@scabug
Copy link
Author

scabug commented Sep 23, 2013

Maurício Szabo (mauricio.szabo) said:
Although I like the consistency of treating constructors as regular methods, I would prefer another correction in place of restricting even more methods (or, in this case, constructors) with default arguments. For instance, in C++ I can create multiple methods with default arguments if the language is able to distinguish one from another:

#include <iostream>
using namespace std;

class Foo {
    public:
    void foo(int foo = 10, int bar = 20) {
        cout << "First method\n";
    }
    
    void foo(string foo, int bar = 20) {
        cout << "Second method\n";
    }
};

int main(void) {
    Foo f = Foo();
    f.foo(10);  //Prints "First method"
    f.foo("String"); //Prints "Second method"
}

In Scala, as I have shown in my examples, this is almost working except that for some cases it detects double definitions. Is it possible to try to correct these double definitions, in place of putting even more restrictions over constructors?

(Sorry for the trouble, but working in Scala it seems cumbersome to work with constructors the way it is implemented today, comparing it with another languages, that I don't think implementing even more restrictions is quite the way to go...)

@scabug
Copy link
Author

scabug commented Sep 23, 2013

@som-snytt said (edited on Sep 24, 2013 4:19:26 AM UTC):

it's not really clear if I do new Foo("A String") if it should call the primary or the secondary

I also did a double-take. The rule is to prefer the application that doesn't require default args, which seems to be a robust rule in the face of this issue.

(I'm so used to Web 2.0 that I didn't notice how stale the comments page had become.)

@scabug
Copy link
Author

scabug commented Sep 24, 2013

@retronym said:
The restrictions are in line with the specification for names/defaults. We generally know that overloading interacts poorly with other language features, but tend to favour these sort of restrictions rather than investing too much into making things work.

Martin recently said:

"For me the conclusion is: Overloading in Scala is not a first-class feature but a "best-effort" feature. If it was not for Java interop we probably would not have it. Consequently I see no motivation to complicate the language just to make overloading work better in some cases."

One pertinent difference between Scala and C++ is our support for separate compilation. Furthermore, defaults are polymorphic (not that this part applies to defaults in constructors). This means that the default getters must have a stable name. This was chosen as default$<method>$N, at the expense of using defaults in more than one overloaded alternative.

@scabug scabug closed this as completed Sep 27, 2013
@scabug scabug added this to the 2.11.0-M6 milestone Apr 7, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants