Skip to content

[Feature] Not require a default value when arguments are given the default value from it's super class #2423

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

Closed
AlexV525 opened this issue Aug 20, 2022 · 3 comments
Labels
feature Proposed language feature that solves one or more problems

Comments

@AlexV525
Copy link

AlexV525 commented Aug 20, 2022

I didn't find any related issues, please link to this if any.

Consider the code:

class A {
  void test({
    String test = 'default',
  }) {}
}

class C extends A {
  @override
  void test({
    String test, // <- missing_default_value_for_parameter
  }) {}
}

The above code will always report an error in the null-safety world (not sure if it's the same in non-null-safety). To avoid the error, we might turn the code into this to avoid redundant hard-code content:

const _defaultTestString = 'default';

class A {
  void test({
    String test = _defaultTestString,
  }) {}
}

class C extends A {
  @override
  void test({
    String test = _defaultTestString,
  }) {}
}

However, in Kotlin, the default value cannot be re-defined if it's overriding a method:

open class A() {
    open fun test(test: String = "default") {}
}

class C() : A() {
    override fun test(
        test: String = "default" // <- Error: An overriding function is not allowed to specify default values for its parameters
    ) {}
}

I think it can be useful for Dart to handle this, or we can make arguments as super arguments (but super here seems in a bad semantic condition):

class A {
  void test({String test = 'default'}) {}
}

class C extends A {
  @override
  void test({super.test}) {}
}
@lrhn lrhn transferred this issue from dart-lang/sdk Aug 20, 2022
@lrhn lrhn added the feature Proposed language feature that solves one or more problems label Aug 20, 2022
@AlexV525
Copy link
Author

If override methods cannot set a default value, it would be a breaking change, which makes the super arguments better for adoptions.

@eernstg
Copy link
Member

eernstg commented Aug 22, 2022

the default value cannot be re-defined if it's overriding a method

First note that there is no choice in Kotlin, because the default value is provided at the call site by the compiler (so if the invoked method m is an override of the declaration of m in the statically known type of the receiver, then the default values in the overriding declaration of m would be ignored, we'd simply use the values from the statically known declaration of m).

In Dart, default values are provided as specified in the actual callee.

class A {
  void m([int i = 42]) {}
}

class B implements A {
  void m([int i = 43]) {}
}

void main() {
  A a = B();
  a.m(); // Will pass 43.
  dynamic d = a;
  d.m(); // Will pass 43.
}

The Dart design enables overriding relations to be more flexible. For instance, we may not want to use the same default value in all subclasses:

abstract class A<X> {
  void m([X x]);
}

class Aint implements A<int> {
  void m([int i = 42]) {/*...*/}
}

class AString implements A<String> {
  void m([String s = 'Hello!']) {/*...*/}
}

class AOther<X> implements A<X> {
  void m([X? x]) {/*...*/} // This is an OK override, using null as the default value.
}

In this situation it is impossible to specify a particular default value for the parameter of A.m (we can't invent a value of type X for all X, and with null safety it certainly can't be null), but we may still wish to specify that the parameter is optional, and this is fine as long as all overriding implementations actually handle the task of providing an admissible default value.

The examples illustrate that we may be able to choose a suitable default value in some cases, and we can override the parameter type to be nullable in order to handle other cases (the implementation of AOther.m will then have to handle null, but that's a necessary price to pay if we want to accept an optional parameter of such a general type).

Another place where it makes a difference is that Dart function types allow for optional parameters to be eliminated by subsumption (that is, a supertype can forget about some or all optional parameters):

void f(int i, {bool b = false, required double d}) {
  print(b);
}

void main() {
  void Function(int, {required double d}) g = f; // OK.
  f(42, d: 0.01); // Prints 'false'.
}

This illustrates that the Dart design relies on having default values that are provided as part of the abstraction itself, not as syntactic sugar for adding some extra arguments at each call site, and also that it is a choice that provides additional expressive power in some ways.

Dart actually used to have a warning at compile time for the situation where an instance method declaration D1 overrides a declaration D0, and one or more optional parameters in D1 correspond to parameters in D0, but they have a different default value. It turned out to be a warning that provides little value, and it was eliminated recently.

Returning to the question about how to express the overriding declaration in the case where we want to reuse the same default value, take a look at the feature proposal in #2269. We could then have this:

class A {
  void test({String test = 'default'}) {}
}

class C extends A {
  @override
  void test({String test = default}) {}
}

default is a new expression which is used to look up the declared default value of a specific declaration. It can be embellished with various things to select that default value from any given function/method, but in this case we just use the "default" meaning of default: When it is used in the declaration of a parameter of an instance method, it means super.default (that is, look up the default value of a parameter in the overridden declaration), and the default value is looked up for the named parameter named test. If we wanted to look up something else then we'd have to write a more verbose construct including default, but in this case the single word default will do.

@AlexV525
Copy link
Author

@eernstg Thanks for the great explanation. I think we can close this since the corresponding proposal already addressed this. 👍

@AlexV525 AlexV525 closed this as not planned Won't fix, can't repro, duplicate, stale Aug 22, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Proposed language feature that solves one or more problems
Projects
None yet
Development

No branches or pull requests

3 participants