Description
Admin comment by @mit-mit:
This proposal now has a feature specification here: super parameters.
This feature enables not having to repeat all parameters from a super class twice. Code previously written like:
class B {
final int foo;
final int bar;
final int baz;
B(this.foo, this.bar, [this.baz = 4]);
}
class C extends B {
C(super.foo, super.bar, [super.baz = 4]) : super(foo, bar, baz);
}
can now be written as:
class C extends B {
C(super.foo, super.bar, [super.baz = 4]);
}
This is likely especially useful is code like Flutter widgets, see for example RaisedButton
.
Original comment by @roy-sianez:
Currently, in Dart, there is syntactic sugar to initialize the property of a class from a constructor parameter:
// Example 1
class Person {
String name;
Person(this.name); // A convenient constructor
}
However, if you want to initialize the property of a superclass, you have to write boilerplate code:
// Example 2
class Employee extends Person {
String department;
Employee(String name, this.department) :
super(name); // Boilerplate
}
I propose adding syntactic sugar where super
could be used instead of this
in the context of a constructor parameter to initialize the property of a superclass, like so:
// Example 3
class Employee extends Person {
String department;
Employee(super.name, this.department); // Easier to read and write
}
Example 3 would be equivalent to example 2.
To prevent the bypassing of important logic in superclass constructors, it could be a requirement that to use this syntactic sugar, the superclass must provide a constructor that uses all this.
or super.
parameters; a class that uses super.
syntactic sugar would delegate to this constructor in its superclass.
This syntactic sugar would be most useful for simple classes where fields are initialized without much preprocessing or logic.
Metadata
Metadata
Assignees
Type
Projects
Status
Activity
lrhn commentedon Sep 11, 2021
This breaks encapsulation.
A superclass maintains an abstraction that is only accessible through its interface and the generative constructor you use.
You can't see if a super-class getter comes from a field or is declared as a getter, or whether a super-constructor parameter is initializing or not.
That ensures that a class like
can safely be refactored to
without any subclass ever noticing.
If you can initialize super-class fields from subclasses then the superclass is locked into having that field.
Abstraction and encapsulation is important. It's inconvenient at times, but I firmly believe that in the long run, the language is better off by being strict about who can see through which abstractions.
Now, if
Foo(super.x);
just meant to passx
implicitly in the superclass constructor, then we might have something which can work (although it breaks the "favor explicit over implicit" rule.)Take
Foo(super.x, thisy, {super.z});
being equivalent toFoo(Type1 x, thisy, {Type2 z}) : super(x, z: z);
, whereType1
is the type of the first parameter of the unnamedsuper
constructor, andType2
is the type if its namedz
parameter.Maybe it only works if you pass all the
super.
parameters to the superclass constructor in the same order, and no other arguments, or maybe we'll need some way to introduce explicit parameters as well.That might be possible. Not sure it's worth the effort by itself, though. Maybe we want other kinds of parameter forwarding as well, and it could be included in a larger feature.
Levi-Lesches commentedon Sep 12, 2021
This is something that's been asked for in several issues. Someone also suggested the ability to specify a certain constructor, if the superclass has multiple constructors, instead of having the compiler try to be smart.
@eernstg suggested in one of the threads that
super.x
notation can be restricted to named parameters only, so as to avoid ambiguity about ordering and types when passing to the super constructor. I think that's a fair concession for such a feature. It lets the compiler be completely unambiguous while letting the subclass decide the order they define the parameters, without being restricted by the order of the superclass.Hopefully these examples help show the logic behind it.
munificent commentedon Sep 13, 2021
I worry that this feature is a little too magical and special... but I have to admit that forwarding parameters to superclass constructors is a really annoying chore in Dart. I often wish superclass constructors were straight up inherited to avoid it, and I have sometimes designed classes to have two-phase initialization (i.e. a separate initializing method in the superclass to pass in its state) just to avoid having to forward all the parameters through the subclass. Here is one example.
The suggested
super.
syntax does look really nice, carries the right signal, and causes no problems in the grammar. I think a reasonable set of semantics could be:super.
in the order that they appear and build a positional argument list.super.
and build a named argument map.It is a compile-error if a constructor parameter list containing a
super.
parameter also contains a superclass constructor invocation in its initializer list.The real question is how useful this sugar would be. My hunch is pretty useful. Actually, I went ahead and wrote a little script to scrape a corpus and try to answer it empirically. It looks for
super
clauses in constructor initializer lists and then determines if that entire clause could be inferred fromsuper.
parameters according to the above rules. In other words, to match:super
argument list must be a simple identifier. Named argument names must match the expression (foo: foo
).If all of those are true, then the
super
clause could be inferred fromsuper.
parameters. The results on a corpus of pub packages are:So of the non-empty
super
clauses in constructor initializer lists, over two-thirds of them could be eliminated if we hadsuper.
arguments. That sounds like a slam dunk to me. From looking at the results, this feature would eliminate a lot of boilerplatesuper(key: key)
lines.If you're curious, the longest
super
clause that would benefit is this heartbreaking monstrosity:In the Flutter repo itself:
Flutter's best/worst example is:
I think we should do it.
Levi-Lesches commentedon Sep 13, 2021
Those stats are quite supportive! One thing I noticed is that while the big one (70%) can be refactored to use
super.
directly, the rest can be reshaped as needed to do so as well, which they might simply not be doing because this feature doesn't exist yet. So theoretically, maybe all of thosesuper()
calls can be eliminated. Two questions:Does this have to be the restriction, or can it just be the default? Some have commented that it would be helpful to be able to manually specify the super constructor after the
:
. See this example:Can you elaborate on this? I'm assuming you mean something like this:
Since each constructor needs to forward its arguments to its super-constructor, multi-level inheritance shouldn't be an issue; the constructor always forwards all
super.
arguments to its direct super-class.cedvdb commentedon Sep 13, 2021
Couldn't you eliminate boiler plate further than having
super.xyz
? I'm thinking about some sort of spread syntax. Or will it be available once we have record / typed maps ?It might feel magical (it really does not), but if you have 20 parameters that are just passed to super those links are really an inconvenience in readability simply because they don't bring meaningful information.
munificent commentedon Sep 13, 2021
Yes! It was a much higher percentage than I expected.
Yeah, you can always add more and more sophisticated syntactic sugar to cover more edge cases, but you reach the point of diminishing returns. Sugar adds to the cognitive load of the language and we always have to be sensitive to minimizing the amount that users need to know to understand a page of Dart code.
I think the fairly simple rules I suggested are probably pretty close to a sweet spot where they cover a lot of cases but aren't that magical.
Sure, we could do that. I don't know if it buys us enough to justify, though.
I mean in the same constructor:
This would be prohibited because now you're trying to specify the same super clause two different ways, explicitly and implicitly in terms of
super.
parameters. We could try to define some way to merge those automatically, but I think that gets too weird.lrhn commentedon Sep 14, 2021
Do we worry about a usability cliff when you need to pass one non-forwarded parameter to a superclass?
Like:
Here we cannot use the
super.foo
notation even if we forward all the remaining parameters.Perhaps we could allow
super(someArguments)
andsuper.named(someArguments)
and then just append thesuper.foo
positional parameters, and all named parameters, to the list.It still breaks forwarding if the extra argument goes after the ones you want to forward, so it isn't entirely general.
munificent commentedon Sep 14, 2021
Yeah, that's a reasonable extension. I considered that but tried to keep things as simple as possible so left it out of my strawman.
I think it's pretty interesting that even without that, the simple proposal still covers most superclass constructor invocations.
jodinathan commentedon Sep 18, 2021
We also tend to avoid the constructor and split the state management.
This feature is golden.
Proposal for super-parameters
munificent commentedon Sep 30, 2021
We spent some time discussion various strategies for merging positional
super.
parameters and explicit superclass constructor call positional arguments. To get some data, I wrote a script to look at constructors in a big corpus of pub packages. The results are here.cedvdb commentedon Oct 2, 2021
I hope the
super.properties
can inherit the dartdoc as, to my knowledge it is not possible to add documentation for super parameters without overriding them.lrhn commentedon Oct 4, 2021
DartDoc is not structured in a way that guarantees that you can inherit parameter documentation.
If you document the parameter in the standard format:
/// The [nameOfParameter] yadda, yadda, cahoots.
we might be able to extract that paragraph, but it might be referring to other parts of the super-constructor as well, and copying that into the subclass constructor documentation won't necessarily work.
45 remaining items