-
Notifications
You must be signed in to change notification settings - Fork 213
const-by-default constructors #3399
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
Comments
I actually think the "const by default context" is more viable than the "const by default constructor". Both have the same issues as usual: if const is optional, not being const is not an error. One small mistake deep in a nested expression, and then the entire tree decides to not be constant, and you get no warning about that, because it's optional. Having to write Then there are the collection issues. Even if we try, the context would have to propagate to the elements expressions, and then it may require analysing more than once, backtracking of a definitely-not-const element of found. In a A "const requested, but not required" context of That's a potentially serious problem, because constant evaluation happens after type inference (all evaluation does, it needs to know the types), but type inference depends on knowing whether an expression is constant. So far, that's been decidable from the context or the expression. In a "prefer-constant" context, the inference wouldn't know which approach to use, and using both (potentially transitively on sub-expressions) breaks the one-pass inference design. I think it's possible to do something like this, but definitely not trivial. |
I'm also worried about the readability issue and the potentially wrong/accidental choice of non-const status. However, it would also be possible to use an IDE based remedy: We could have two transformations, say,
Similarly, For instance, using the code from the original posting of this issue: Original expression const Center(
child: Column(
children: <Widget>[
Expanded(
child: ColoredBox(
color: Colors.amber,
child: Center(
child: Text('Above'),
),
),
),
Divider(
height: 20,
thickness: 5,
indent: 20,
endIndent: 0,
color: Colors.black,
),
Align(
alignment: AlignmentDirectional.centerStart,
child: Text(
'Subheader',
textAlign: TextAlign.start,
),
),
Expanded(
child: ColoredBox(
color: Colors.blue,
/*54*/ //color: Theme.of(context).colorScheme.primary,
child: Center(
child: Text('Below'),
),
),
),
],
),
); Now we edit the code such that Now perform `lower-const`, twice, on the top-level expression. Center(
child: Column(
children: <Widget>[
const Expanded(
child: ColoredBox(
color: Colors.amber,
child: Center(
child: Text('Above'),
),
),
),
const Divider(
height: 20,
thickness: 5,
indent: 20,
endIndent: 0,
color: Colors.black,
),
const Align(
alignment: AlignmentDirectional.centerStart,
child: Text(
'Subheader',
textAlign: TextAlign.start,
),
),
const Expanded(
child: ColoredBox(
//color: Colors.blue,
/*54*/ color: Theme.of(context).colorScheme.primary,
child: Center(
child: Text('Below'),
),
),
),
],
),
); Finally, perform `lower-const` twice again on the last `const` expression and remove `const` from line 54, or just edit those changes directly. Expanded(
child: ColoredBox(
//color: Colors.blue,
/*54*/ color: Theme.of(context).colorScheme.primary,
const child: Center(
child: Text('Below'),
),
), The point is that the effect of those two transformations is easy to understand, and they will perform some editing operations that are relevant to the situation described in this issue, as well as other situations where there is a need to manipulate the occurrences of |
Fundamentally, I think of this as a performance problem. Flutter and Flutter users want I generally dislike the approach that Dart takes to const, but I'm very hesitant to make performance-driven changes without good performance data we can use to evaluate it. |
I don't see any "zero effort" approach that will not have pitfalls. If the client of an API doesn't have to do anything, and will still gets as many constant expressions as possible, then it's still possible that they get zero, and it won't be noticed. This sounds more like a lint.
So say that if a parameter or variable (or expression context in general) is marked with An expression is maximally constant iff
An expression is made maximally constant (whether it's currently a constant expression or not) by traversing every subexpression:
(Update the We can allow an explicit |
I believe it would be massively breaking, since sometimes you just want different objects, even if their constructor marked I don't understand why running |
Today, a
const
constructor for classFoo
can be invoked asnew Foo()
,const Foo()
or simplyFoo()
.In the last case, the invocation defaults to
new Foo()
unless the expression is in aconst
context, in which case it can only beconst
, so it defaults toconst
.In the case of Flutter widget trees, the defaulting to
new Foo()
is the wrong choice. It would be better to default toconst Foo()
if at all possible.There are several lints that try to help the developer work around the lack of const-by-default:
prefer_const_constructors
to encourage the developer to putconst
at each constructor in the widget tree expressionprefer_const_literals_to_create_immutables
to encourage the developer to putconst
on some subexpressions even when the constructor is notconst
.unnecessary_const
to help the developer removeconst
from a const context.If the developer has a
const
expression and changes some deep sup-expression to be non-const, the expression is now an error. The developer removes the highconst
to fix the error. The lints now encourage addingconst
all along the side-trees of the spine of the expression tree leading to the original edit.Example
As an example of this experience, copy the following program into dartpad.
Now use a computed color: uncomment line 54 and comment-out line 53.
You will see 7 errors for 'invalid const value'.
The remedy is to remove the
const
at line 26.You will now see 9 lint warnings to prefer
const
.The remedy is to add
const
in 4 places.The lints have quick-fixes to help with this process, but pushing
const
around the code would be completely unnecessary if the widget constructors defaulted toconst
whenever possible.Questions
Should const-by-default be the default behaviour? This would break
identical(Object(), Object())
. One can always 'escape' the behaviour with an explicitnew
, but this idea is probably too breaking.If const-by-default is not the default behaviour, should the language add an opt-in syntax, e.g. putting the pseudo-keyword
prefer
in front ofconst
, i.e.The opt-in could be via a class modifier:
const class Foo { }
. This makes all constructors const-by-default.A class or constructor opt-in would nicest for Flutter - the preference for
const
is really an implementation detail of Flutter, so should be 'batteries included' as much as possible.Should a const-by-default constructor that can't be
const
because of one argument make the other argument expressions be const if possible? e.g. shouldFoo(color: computed(), children: [Foo()])
make thechildren:
list be const?Should a non-const constructor (or for that matter, a static method) be able to impose a const-by-default context on an argument by adding
const
orprefer const
to the parameter declaration?Another option would be to have syntax to introduce a const-by-default context, say,
const?
. In the big example, writingreturn const? Center(...
would infect the whole expression with const-by-default. I think this is worse than the above ideas, mainly because the Flutter developer still has to do something.The text was updated successfully, but these errors were encountered: