-
Notifications
You must be signed in to change notification settings - Fork 217
Description
This is a proposal to add a new kind of constant expressions:
Assume that
e
is a potentially constant expression andv
is an identifier; in this casee.v
is potentially constant. Assume thate
is a constant expression that evaluates to an instance of a classC
that declares a final instance variablev
. In this casee.v
is a constant expression.
With this definition, evaluation of e.v
is guaranteed to be an invocation of the implicitly induced getter named v
. This does not involve execution of user-written code, and it can be done at compile time.
This allows constant expressions to use a small amount of the expressive power which is available to non-constant expressions, but has so far been out of reach for constant expressions. For example:
class Person {
final String firstName, lastName;
const Person(this.firstName, this.lastName);
}
class Address {
final String fullName;
// ...
const Address.fromNames(String firstName, String lastName): fullName = '$firstName $lastName ...';
const Address.fromPerson(Person person): fullname = '${person.firstName} ${person.lastName} ...';
}
void main() {
const person = Person('John', 'Doe');
const address1 = Address.fromNames(person.firstName, person.lastName);
const address2 = Address.fromPerson(person);
}
With the current rules we have to use an approach like in address1
, because we can't pass multiple constant objects to the Address
constructor using a single object which is then explored.
With this feature, we can use the more encapsulated approach of address2
. In this case, person
is passed to the Address
constructor which will then look up its members as needed in order to arrive at the same result.
Another case which is covered as a subset of this feature is the proposal in #2780, which is about enumerated values.
All in all, it seems both benign and useful to allow constant expressions to support this very fundamental kind of abstraction.
Update: Based on @lrhn's comment, I think we should require an explicit marker, such as const
, on any getter which is intended to be constant, and on any variable whose implicitly induced getter should be constant. The explicit marker is needed because we would otherwise rely on implementation details of getters in order to accept a given getter as constant, and that would be too easy to break by accident.
Also note the generalization mentioned here which will allow many explicitly written getters to be constant.
As a result of these changes, the following would be supported:
class Person {
const String _name1, _name2;
const bool _reversed;
const Person(String personName, String familyName, {bool reversed = false})
: _name1 = name1, _name2 = name2, _reversed = reversed;
const String get firstName => reversed ? _name2 : _name1;
const String get lastName => reversed ? _name1 : _name2;
String toString() => "$firstName $lastName";
}
Note that we omit final
on variable declarations which are declared to have a constant getter (by having const
), because it would be an error for such a variable to be non-final.