|
|
Created:
3 years, 6 months ago by Lasse Reichstein Nielsen Modified:
3 years, 6 months ago Reviewers:
floitsch CC:
reviews_dartlang.org Target Ref:
refs/heads/master Visibility:
Public. |
DescriptionAdd informal specification for optional-new/const, constructor tearoffs.
R=floitsch@google.com
Status: under discussion
Committed: https://github.com/dart-lang/sdk/commit/52691c6937b4de7ac3caebcb340282ffe1d84f6d
Patch Set 1 #
Total comments: 22
Patch Set 2 : Update structure. #Patch Set 3 : Address comments #
Total comments: 10
Patch Set 4 : Version number #Patch Set 5 : Add more text #Messages
Total messages: 10 (2 generated)
lrn@google.com changed reviewers: + floitsch@google.com
LGTM. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... File docs/language/informal/optional-new-const.md (right): https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:14: In current Dart code, some expressions are required to be compile-time constant expressions. I would start this section differently: In current Dart code, every const expression (except for annotations) must be prefixed with a `const` keyword. This is the case, even when the context requires the expression to be a compile-time constant expression. For example, inside a const list or map, all elements must be compile-time constants. This leads to repeated `const` keywords in nested expressions: ``` dart const dictionary = const { "a": const ["able", "apple", "axis"], "b": const ["banana", "bold", "burglary"], }; ``` Here ... https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:16: These are mainly sub-expressions of other compile-time constant expressions, but also the initializer expressions of const variables, default values for optional parameters, switch case expressions, and (sub-expressions of) metadata annotations. These are mainly, -> "for example" ? https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:59: * is a sub-expression of an expression in a const context. Should this include the annotations? https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:87: One thing that needs to be mentioned: ``` var x = A(499); ``` where `A` is a const class. Would that be a `new` or `const` call? We have lots of `new Future.delayed(const Duration(...), ...)`. It would be nice to avoid writing the `const` in the future. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:219: where `Foo` and `prefix.Foo` denotes a class and `bar` is a constructor of `Foo`, and the expression is not followed by arguments `(args)`, is no longer an error. Not yet for this document, but we might need to update for constructors with generic arguments. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:273: map.putIfAbsent(42, List<int>.new); // Rather than map.putIfAbsent(42, () => <int>[]) Wouldn't put that example. I don't think it reads better. Also, we are discussing const functions, so this would just stay the way it is now. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:309: Also, if the class type parameters are visible in members, including getters and setters, it should mean that that *static fields* would have to exist for each instantiation, not just once. That's so incompatible with the current behavior, and most likely completely unexpected to users. This idea is unlikely to ever happen. Was that all we had against it? There weren't other issues? I remember, that we pretty much shelved this approach.
https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... File docs/language/informal/optional-new-const.md (right): https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:14: In current Dart code, some expressions are required to be compile-time constant expressions. On 2017/06/08 15:06:43, floitsch wrote: > I would start this section differently: > In current Dart code, every const expression (except for annotations) must be > prefixed with a `const` keyword. This is the case, even when the context > requires the expression to be a compile-time constant expression. > > For example, inside a const list or map, all elements must be compile-time > constants. This leads to repeated `const` keywords in nested expressions: > > ``` dart > const dictionary = const { > "a": const ["able", "apple", "axis"], > "b": const ["banana", "bold", "burglary"], > }; > ``` > > Here ... Acknowledged. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:16: These are mainly sub-expressions of other compile-time constant expressions, but also the initializer expressions of const variables, default values for optional parameters, switch case expressions, and (sub-expressions of) metadata annotations. On 2017/06/08 15:06:43, floitsch wrote: > These are mainly, -> "for example" ? Acknowledged. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:59: * is a sub-expression of an expression in a const context. It absolutely has to, and it does (`@Symbol(...)` is included above with constructor invocations). https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:87: Ah, you are thinking of the "implicit const when calling const constructor with const arguments" idea, which is separate from the "const context" idea. That was not intended as part of this, but we can add it as well - if we can agree on it. I remember having issues with it, but I don't remember which :) One issue is obviously that the two intersect. Take `const A(const [42])`. If you wrote `const A([42])` then the `[42]` would be in a const context and would be const itself. If you write `A(const [42])` then the argument to the const constructor would be const, and this would implicitly make the entire thing const. Without either, it would probably have to default to an implicit `new`. That means that removing either `const` will still preserve constness, but removing both won't. That's tricky. It's not unprecedented. It's like type inference, we have outside-in inference and inside-out inference, and if you have neither, you have to default to dynamic. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:219: where `Foo` and `prefix.Foo` denotes a class and `bar` is a constructor of `Foo`, and the expression is not followed by arguments `(args)`, is no longer an error. Can we do a specializing tear-off of those? That is, can we do: Function fooBar = Foo.bar<int>; (Like I think we are supposed to be able to do with generic functions). https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:273: map.putIfAbsent(42, List<int>.new); // Rather than map.putIfAbsent(42, () => <int>[]) On 2017/06/08 15:06:43, floitsch wrote: > Wouldn't put that example. I don't think it reads better. > > Also, we are discussing const functions, so this would just stay the way it is > now. Acknowledged. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:309: Also, if the class type parameters are visible in members, including getters and setters, it should mean that that *static fields* would have to exist for each instantiation, not just once. That's so incompatible with the current behavior, and most likely completely unexpected to users. This idea is unlikely to ever happen. I don't remember any other big issues (but I could easily have forgotten). I'm not even sure this is an issue - an implicit static field getter will just not use the type parameter except as a type guard, so all instantiations can share the same storage. Say: class C<T> { static T x; } C<int>.x = 42; print(C<String>.x); We can probably make that work by adding `as T` guards to the implicit getter and setter and actually using `Object` as the storage type, it's just *not useful*. In general, it wasn't really useful. What we could do instead is to move the type parameters onto constructors. class C<T> { C<T> new.foo<T>() => ... } Then we don't write C<int>.foo but C.foo<int> to tear-off the constructor. It also means that we can actually make constructors create differently parameterized types than their type parameters, or put constraints on individual constructors: Say: Set<T> Set.ordered<T extends Comparable>([List<T> elements]) That's not possible with the current constructors.
https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... File docs/language/informal/optional-new-const.md (right): https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:59: * is a sub-expression of an expression in a const context. On 2017/06/08 15:50:22, Lasse Reichstein Nielsen wrote: > It absolutely has to, and it does (`@Symbol(...)` is included above with > constructor invocations). right. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:87: On 2017/06/08 15:50:22, Lasse Reichstein Nielsen wrote: > Ah, you are thinking of the "implicit const when calling const constructor with > const arguments" idea, which is separate from the "const context" idea. That was > not intended as part of this, but we can add it as well - if we can agree on it. > > I remember having issues with it, but I don't remember which :) > > One issue is obviously that the two intersect. Take `const A(const [42])`. > If you wrote `const A([42])` then the `[42]` would be in a const context and > would be const itself. If you write `A(const [42])` then the argument to the > const constructor would be const, and this would implicitly make the entire > thing const. Without either, it would probably have to default to an implicit > `new`. That means that removing either `const` will still preserve constness, > but removing both won't. That's tricky. > > It's not unprecedented. It's like type inference, we have outside-in inference > and inside-out inference, and if you have neither, you have to default to > dynamic. It's independent, but if we make `new` optional, then we need to decide now what it should be. I agree that there are some annoying corner cases, but all in all, users would normally not see the difference. Mostly, they would just get canonicalized objects from time to time. In general I would actually recommend to write `new` if you rely on identity, so that shouldn't matter. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:219: where `Foo` and `prefix.Foo` denotes a class and `bar` is a constructor of `Foo`, and the expression is not followed by arguments `(args)`, is no longer an error. On 2017/06/08 15:50:22, Lasse Reichstein Nielsen wrote: > Can we do a specializing tear-off of those? > That is, can we do: > Function fooBar = Foo.bar<int>; > (Like I think we are supposed to be able to do with generic functions). > We should be able to do that. We also want to automatically make them non-generic if the typing context requires it. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:309: Also, if the class type parameters are visible in members, including getters and setters, it should mean that that *static fields* would have to exist for each instantiation, not just once. That's so incompatible with the current behavior, and most likely completely unexpected to users. This idea is unlikely to ever happen. On 2017/06/08 15:50:22, Lasse Reichstein Nielsen wrote: > I don't remember any other big issues (but I could easily have forgotten). > > I'm not even sure this is an issue - an implicit static field getter will just > not use the type parameter except as a type guard, so all instantiations can > share the same storage. > Say: > class C<T> { > static T x; > } > C<int>.x = 42; > print(C<String>.x); > We can probably make that work by adding `as T` guards to the implicit getter > and setter and actually using `Object` as the storage type, it's just *not > useful*. > > In general, it wasn't really useful. > > What we could do instead is to move the type parameters onto constructors. > class C<T> { > C<T> new.foo<T>() => ... > } > Then we don't write C<int>.foo but C.foo<int> to tear-off the constructor. > > It also means that we can actually make constructors create differently > parameterized types than their type parameters, or put constraints on individual > constructors: > > Say: > Set<T> Set.ordered<T extends Comparable>([List<T> elements]) > That's not possible with the current constructors. > > Rereading it now, that is actually enough. Having a separate static field for every generic type is not really acceptable.
https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... File docs/language/informal/optional-new-const.md (right): https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:87: From this proposal, it would add `new` in front of any constructor invocation that is not in a const context, so no discussion there. The feature you are talking about is not independent since it would change that to adding `const` in some cases that are not in a const context. I've discussed it more in the "related features" section. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:219: where `Foo` and `prefix.Foo` denotes a class and `bar` is a constructor of `Foo`, and the expression is not followed by arguments `(args)`, is no longer an error. I'm not writing anything about that for now. If we add the syntax anyway, we'll just have one less syntax change here. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:273: map.putIfAbsent(42, List<int>.new); // Rather than map.putIfAbsent(42, () => <int>[]) But I don't have a better example, so suggestions appreciated. https://codereview.chromium.org/2929753003/diff/1/docs/language/informal/opti... docs/language/informal/optional-new-const.md:309: Also, if the class type parameters are visible in members, including getters and setters, it should mean that that *static fields* would have to exist for each instantiation, not just once. That's so incompatible with the current behavior, and most likely completely unexpected to users. This idea is unlikely to ever happen. On 2017/06/08 17:30:08, floitsch wrote: > On 2017/06/08 15:50:22, Lasse Reichstein Nielsen wrote: > > I don't remember any other big issues (but I could easily have forgotten). > > > > I'm not even sure this is an issue - an implicit static field getter will just > > not use the type parameter except as a type guard, so all instantiations can > > share the same storage. > > Say: > > class C<T> { > > static T x; > > } > > C<int>.x = 42; > > print(C<String>.x); > > We can probably make that work by adding `as T` guards to the implicit getter > > and setter and actually using `Object` as the storage type, it's just *not > > useful*. > > > > In general, it wasn't really useful. > > > > What we could do instead is to move the type parameters onto constructors. > > class C<T> { > > C<T> new.foo<T>() => ... > > } > > Then we don't write C<int>.foo but C.foo<int> to tear-off the constructor. > > > > It also means that we can actually make constructors create differently > > parameterized types than their type parameters, or put constraints on > individual > > constructors: > > > > Say: > > Set<T> Set.ordered<T extends Comparable>([List<T> elements]) > > That's not possible with the current constructors. > > > > > > Rereading it now, that is actually enough. > Having a separate static field for every generic type is not really acceptable. Done.
Still LGTM. https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... File docs/language/informal/optional-new-const.md (right): https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:41: Omitting the `const` prefix from list and map literals does not introduce a need for new syntax, it's just the plain list and map literal syntax. for new syntax, since that syntax is already used for plain list and map literals. https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:45: To allow all const constructor invocations to omit the `const`, the grammar needs to be extended to handle the case of `Nyclass<SomeType>.name(arg)`. Myclass ? https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:133: We could potentially change that, re-purpose the plain `Foo` to refer to the constructor and introduce a new syntax for the `Type` object for the class, say the Java-esque `Foo.class`. It would be a major breaking change, though, even if it could be mechanized. We should consider whether it's feasible to make this change. I would move this into a separate section. "Alternatives" or something similar. https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:139: This tear-off syntax is something we want in any case, independently of the optional new/const changes above. However, the syntax completely subsumes the optional `ne` feature; with tear-off syntax, `Foo.bar(42)` is just the tear-off `Foo.bar` expression called as a function. You'd have to write `Foo.new(42)` instead of just `Foo(42)` (which is an argument for re-purposing the `Foo` expression to refer to the constructor instead of the type). optional `new` feature. (missing "w") https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:295: map.putIfAbsent(42, List<int>.new); // Rather than map.putIfAbsent(42, () => <int>[]) map.putIfAbsent(42, HashSet<int>.new); // Rather than ...
https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... File docs/language/informal/optional-new-const.md (right): https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:41: Omitting the `const` prefix from list and map literals does not introduce a need for new syntax, it's just the plain list and map literal syntax. On 2017/06/19 13:33:48, floitsch wrote: > for new syntax, since that syntax is already used for plain list and map > literals. Done. https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:45: To allow all const constructor invocations to omit the `const`, the grammar needs to be extended to handle the case of `Nyclass<SomeType>.name(arg)`. On 2017/06/19 13:33:48, floitsch wrote: > Myclass ? Done. https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:133: We could potentially change that, re-purpose the plain `Foo` to refer to the constructor and introduce a new syntax for the `Type` object for the class, say the Java-esque `Foo.class`. It would be a major breaking change, though, even if it could be mechanized. We should consider whether it's feasible to make this change. On 2017/06/19 13:33:48, floitsch wrote: > I would move this into a separate section. "Alternatives" or something similar. Done. https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:139: This tear-off syntax is something we want in any case, independently of the optional new/const changes above. However, the syntax completely subsumes the optional `ne` feature; with tear-off syntax, `Foo.bar(42)` is just the tear-off `Foo.bar` expression called as a function. You'd have to write `Foo.new(42)` instead of just `Foo(42)` (which is an argument for re-purposing the `Foo` expression to refer to the constructor instead of the type). On 2017/06/19 13:33:48, floitsch wrote: > optional `new` feature. (missing "w") Done. https://codereview.chromium.org/2929753003/diff/40001/docs/language/informal/... docs/language/informal/optional-new-const.md:295: map.putIfAbsent(42, List<int>.new); // Rather than map.putIfAbsent(42, () => <int>[]) Done. Also removed the `new` from the "rather than" sections, since that's going away anyway, and it makes the unnecessary eta-expansion much more visible.
Description was changed from ========== Add informal specification for optional-new/const, constructor tearoffs. Status: under discussion ========== to ========== Add informal specification for optional-new/const, constructor tearoffs. R=floitsch@google.com Status: under discussion Committed: https://github.com/dart-lang/sdk/commit/52691c6937b4de7ac3caebcb340282ffe1d84f6d ==========
Message was sent while issue was closed.
Committed patchset #4 (id:60001) manually as 52691c6937b4de7ac3caebcb340282ffe1d84f6d (presubmit successful). |