|
|
Created:
3 years, 8 months ago by eernst Modified:
3 years, 5 months ago CC:
reviews_dartlang.org, floitsch Target Ref:
refs/heads/master Visibility:
Public. |
DescriptionAdded informal generic method syntax and generic function type specs.
R=lrn@google.com
Committed: https://github.com/dart-lang/sdk/commit/5904409d50fa9b800d015faadbe49cb13e358644
Patch Set 1 #
Total comments: 2
Patch Set 2 : Added files were HTML encoded, are now markdown source #Patch Set 3 : Reformatted to fit in 80 columns, except one url #
Total comments: 39
Patch Set 4 : Review response #
Total comments: 10
Patch Set 5 : Updates about generic function type aliases inserted #Patch Set 6 : Review response #Patch Set 7 : Added wording on how to treat ty-vars in an `on` clause #
Total comments: 53
Patch Set 8 : Review response #
Total comments: 7
Patch Set 9 : Review response on generic-method-syntax.md #Patch Set 10 : More review response #Patch Set 11 : Yet more review response #Patch Set 12 : Added paragraph on tricky typedef type arg bounds #
Messages
Total messages: 23 (3 generated)
eernst@google.com changed reviewers: + lrn@google.com
These informal specs have been implemented, and they haven't been modified for a while, so they are similar to the other informal specs that we have already moved to the same directory in the SDK repo.
https://codereview.chromium.org/2841483003/diff/1/docs/language/informal/gene... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/1/docs/language/informal/gene... docs/language/informal/generic-function-type-alias.md:11: The document has extension .md, but contains HTML. That is probably a mistake :)
Fixed various formatting issues. https://codereview.chromium.org/2841483003/diff/1/docs/language/informal/gene... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/1/docs/language/informal/gene... docs/language/informal/generic-function-type-alias.md:11: On 2017/04/24 13:02:05, Lasse Reichstein Nielsen wrote: > The document has extension .md, but contains HTML. > That is probably a mistake :) Fixed.
https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:56: metadata identifier (('extends'|'super') type)? Remove `super`. We are not planning on adding it right now. If this is being treated as an *informal specification*, it should only specify the actual features that implementations must implement. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:89: a left parenthesis (in short, we are "looking at `< .. >(`"). Drop the "whenever". *The ambiguity* is always resolved in favor of the generic function invocation. If there is no ambiguity, there is nothing to resolve. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:91: This implies that an expression like `foo(a<b,2>(d))` will be rejected What does "rejeceted" mean? In any case, remove entire section. This will parse as two comparisons, and it currently does in both the VM, dart2js and the analyzer. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:116: parser to perform the required check. Drop this section, or rewrite it to suggest, as an optimization, to check at foo< whether it's followed by a matching '>' and a '(', otherwise it cannot be a call and disambiguation thus can be done early. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:127: instance and type arguments for the constructor itself. It is possible that The second sentence seems to assume that constructors, setters, getters and operators could be generic, otherwise it's not interesting to say that you can't pass the type parameters. I'm not sure what it's trying to say - is it a reason for not having generic constructors, etc. or is it a problem that would have to be solved if we did have them? And why are we saying that, if we don't have them anyway? Probably just drop the sentence. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:129: extension. Not really, no. Setters and getters will not be generic, and operators most likely not either. Constructors could be, but then say that it's possible that the restriction on constructors can be lifted at a later point. Don't say that this isn't a fully-fledged feature. We don't want people speculating that it might change, because most likely it won't. If we add more, it's a different feature. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:135: support them. Drop mentions of lower bounds. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:141: reification is not required. This disagrees with the paragraph above saying that this is an informal specification for implementors. We are going for the final version now, the one that is actually being implemented. It may change due to feedback, but it's not "just for experiments". So, remove paragraph. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:201: the value yielded by an evaluation of `dynamic`, following the treatment of if `dynamic` can be shadowed, this it's correct to talk about "an evaluation of `dynamic`". Also "evaluation" generally applies to expressions, not to types. Maybe just "they will yield the dynamic type". https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:205: and `X` used as a type literal proceeds as if `X` had been a malformed type, a type literal -> an expression I'm not sure exactly what a "type literal" is (the spec doesn't define it), so I prefer to only use it for an identifier that resolves to a type declaration (class, typedef), not a type variable. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:211: program with a different runtime semantics for these expressions. The If it's specified as a compile-time error, then compilers are not free to just implement a different runtime behavior. I'm all for a "--run-with-errors" flag that transforms broken programs into running ones, but it must be explicit and opt-in that you are not getting the correct semantics. So, no, this is incorrect as written. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:219: generic class instantiation `G` which is itself used in `e is G`, `e is! G`, Isn't this just saying that in a generic class instantiation, a malformed type is treated as dynamic? After that, the resulting type is unrelated to the original type parameter. It confuses me (a little) that the special cases are mentioned because G means the same type in every setting, and that type doesn't depend on the type *variable* X, only on the type it refers to. It's like talking about a parameter and then mentioning the argument *expression* used to pass the value - it should be irrelevant. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:233: to the constructor will be the value yielded by an evaluation of `dynamic`. Drop "the value yielded by an evaluation of `dynamic`", just "the dynamic type". If that's not the same, something is very, very wrong, and the latter is much clearer. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:240: succeed or fail as if `X` had been the type `dynamic`. This is different from a malformed type, which would throw if used as a type annotation. Maybe say that.
Reminder to self: Consider adding something on static typing of the defined entities (including generic function types, their subtype relationships, where they can be used). Currently the informal spec on generic function type aliases only deals with the static checking of the `typedef` itself.
Review response. A couple of things in here need further discussion, lots of other things should be resolved by the new patch. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:56: metadata identifier (('extends'|'super') type)? On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > Remove `super`. We are not planning on adding it right now. If this is being > treated as an *informal specification*, it should only specify the actual > features that implementations must implement. Done. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:89: a left parenthesis (in short, we are "looking at `< .. >(`"). On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > Drop the "whenever". *The ambiguity* is always resolved in favor of the generic > function invocation. If there is no ambiguity, there is nothing to resolve. That was not the approach that I specified, I took a different route because of the performance implications. It was my impression that we agreed on that approach. I'm now learning that the implementations haven't done that, but first I'll explain why that approach was taken. The point is that it may be expensive if the parser needs to perform speculative parsing into a lookahead whose length has no finite constant bound (there's no limit on the slowdown if we're in danger of speculatively parsing a lookahead of arbitrary length "for nearly every token"). Presumably, expressions (including subexpressions) are common, so if we need to check for `primary<typeList>(` all the time, it might be costly. We're doing top-down parsing and hence, for the standard example `foo(a<b,c>(d))` with a focus on the `argumentList`, we need to make a choice at step 3: argumentList --> expressionList --> expression --> ... versus: argumentList --> expressionList --> expression ',' expression --> ... in a derivation with many steps to reach "a<b, c>(d)" as two relational expressions respectively as one function invocation. I just created a derivation and it takes 44 resp. 47 steps, where each step derives as many non-terminals in parallel as possible, e.g., going `shiftExpression '<' shiftExpression ',' ..` to `additiveExpression '<' additiveExpression ',' ..`). So, basically, we must traverse a more than 40 level deep call tree whenever we have an expressionList in order to disprove that it matches `primary typeParameters '('`, in order to see how long the expressionList is. Is that really no problem? Nevertheless, the tools may be doing just that whenever needed, which would of course serve as an argument that it _is_ really no problem! In that case we will just decide that the ambiguity is resolved in favor of a generic function invocation, rather than saying that the tools can use a well-defined O(1) shortcut to commit to a generic function invocation, even in some cases where it would have parsed as two relational expressions but fails as a generic function invocation (e.g., `foo(a<b,2>(d))`, where we will then get a spurious syntax error). However, I'm not 100% convinced that it will be trivial to detect what the parsers are actually doing. Maybe they are using some other shortcut which may give a different set of spurious failures? Can we live with that? So I'd like to clarify this issue rather than just declaring that there is no performance problem, and tools must simply decide the ambiguous cases perfectly, and then favor the call in the ambiguous case and otherwise parse the construct which is unambiguously there. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:91: This implies that an expression like `foo(a<b,2>(d))` will be rejected On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > What does "rejeceted" mean? It is a syntax error. > In any case, remove entire section. This will parse as two comparisons, and it > currently does in both the VM, dart2js and the analyzer. As mentioned, I'm surprised that this is the case, and I'm not 100% convinced that they can do that in a way which is correct and still has acceptable performance. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:116: parser to perform the required check. On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > Drop this section, or rewrite it to suggest, as an optimization, to check at > foo< whether it's followed by a matching '>' and a '(', otherwise it cannot be a > call and disambiguation thus can be done early. This should be commentary; changed the font to italic. It is of course just a suggestion for a possible optimization (even though that optimization may be crucial in practice). https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:127: instance and type arguments for the constructor itself. It is possible that On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > The second sentence seems to assume that constructors, setters, getters and > operators could be generic, otherwise it's not interesting to say that you can't > pass the type parameters. The second sentence is intended to explain why constructors, setters, getters, and operators cannot be generic. Changed the '.' to ':' such that it is evident that sentence 2 gives the reason for the rule given in sentence 1. > I'm not sure what it's trying to say - is it a reason for not having generic > constructors, etc. Yes, it's a motivation for why we don't have them. You could say that we could still have them even though "they can't be called with type arguments", because they type arguments might be inferred. However, I think that it is a reasonably trade-off to say that we don't want to support generic getters etc. _because_ there is no way to call them with explicit type arguments. On top of that comes that it may be useless (I don't know any good use cases for generic getters), but there might still be some use cases that we haven't noticed yet. > or is it a problem that would have to be solved if we did > have them? And why are we saying that, if we don't have them anyway? > Probably just drop the sentence. It's clearly '\rationale{}` stuff, and we are explaining why we don't support certain things because we don't support them and someone might wonder why (which is a quite typical role for a `\rationale{}`, I think). Still want to delete it? https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:129: extension. On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > Not really, no. > Setters and getters will not be generic, and operators most likely not either. > Constructors could be, but then say that it's possible that the restriction on > constructors can be lifted at a later point. > > Don't say that this isn't a fully-fledged feature. We don't want people > speculating that it might change, because most likely it won't. If we add more, > it's a different feature. This is exactly like the spec when it says that the restrictions on mixins may be lifted. There is no promise that it'll be right now, nor even that it will ever happen. But we're pretty close to a decision where named constructors can be made generic, so it's not an outrageous thought that we can add some of these things. Changed 'full-fledged' to 'future' in order to avoid the direct implication that the omitted features "ought to" be there. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:135: support them. On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > Drop mentions of lower bounds. Done. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:141: reification is not required. On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > This disagrees with the paragraph above saying that this is an informal > specification for implementors. > We are going for the final version now, the one that is actually being > implemented. It may change due to feedback, but it's not "just for experiments". > So, remove paragraph. Done. I inserted a shorted paragraph explaining that actual type arguments are not reified, since that's still true for this mechanism, and mentioned that a future extension (which is actually upcoming) may add reification. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:201: the value yielded by an evaluation of `dynamic`, following the treatment of On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > if `dynamic` can be shadowed, this it's correct Incorrect? > to talk about "an evaluation of > `dynamic`". https://github.com/dart-lang/sdk/issues/29125 and https://github.com/dart-lang/dart-lang-evolution/issues/141 have not reached a conclusion, so I'll try to avoid the issue and use "the type dynamic" as you suggest (the spec uses that phrase lots of times). (My stance at this point is that `dynamic` currently _must_ be exported from 'dart:core': being a built-in identifier it must be subject to lookups, because otherwise it makes no sense to say that we _cannot_ declare types with that name, because if we also cannot declare non-types then the name is actually a reserved word; so we need to look it up somewhere, and that can't be anywhere else than in 'dart:core'. Oh, well, whatever. ;-) > Also "evaluation" generally applies to expressions, not to types. > Maybe just "they will yield the dynamic type". Right, I was specifically thinking about the dynamic semantics here, and in particular routine type variables `X` used as an expression. But I can see that it matches less perfectly with `e is X` etc. Adjusted accordingly. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:205: and `X` used as a type literal proceeds as if `X` had been a malformed type, On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > a type literal -> an expression > I'm not sure exactly what a "type literal" is (the spec doesn't define it), so I > prefer to only use it for an identifier that resolves to a type declaration > (class, typedef), not a type variable. Done. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:211: program with a different runtime semantics for these expressions. The On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > If it's specified as a compile-time error, then compilers are not free to just > implement a different runtime behavior. > I'm all for a "--run-with-errors" flag that transforms broken programs into > running ones, but it must be explicit and opt-in that you are not getting the > correct semantics. > > So, no, this is incorrect as written. If we insist that the language specification must _not_ specify a semantics for any programs with faults (so there are no warnings, only errors, and, of course, no specification of a semantics for programs with errors), and we insist that compilers are _not_ allowed to implement a run-time behavior for programs with errors, then we have actually decided that there is no conforming way for a tool chain to support execution of any less-than-statically-perfect programs. We hardly want that, and I certainly don't. I think we must leave one door open, and I think it makes sense to say that the open door is the following: Compilers _are_ allowed to compile some programs with errors (according to the analyzer, which will declare exactly the things to be errors that the spec says). And the spec isn't supposed to specify the semantics of faulty programs, so it can't say more than "compilers are allowed to generate code for some faulty programs". I don't think we have discussed `--run-with-errors` before, but it makes sense to have that. But that would still be a tool thing, not a spec thing. In particular, one compiler might take such an option, another one might emit 'error hints' whenever it encounters an error for which it is able to generate some code, etc. I don't think it's a big issue to agree on something in that area. But do we really disagree about the fundamental choice that compilers can make the choice to compile some faulty programs? In the light of all this, I've adjusted the paragraph. ;) https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:219: generic class instantiation `G` which is itself used in `e is G`, `e is! G`, On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > Isn't this just saying that in a generic class instantiation, a malformed type > is treated as dynamic? After that, the resulting type is unrelated to the > original type parameter. > It confuses me (a little) that the special cases are mentioned because G means > the same type in every setting, and that type doesn't depend on the type > *variable* X, only on the type it refers to. > > It's like talking about a parameter and then mentioning the argument > *expression* used to pass the value - it should be irrelevant. Right, this can be compressed. Did that. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:233: to the constructor will be the value yielded by an evaluation of `dynamic`. On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > Drop "the value yielded by an evaluation of `dynamic`", just "the dynamic type". > If that's not the same, something is very, very wrong, and the latter is much > clearer. Adjusted the sentence in approximately that manner. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:240: succeed or fail as if `X` had been the type `dynamic`. On 2017/04/26 09:15:31, Lasse Reichstein Nielsen wrote: > This is different from a malformed type, which would throw if used as a type > annotation. Maybe say that. Removed the phrase `which .. annotation`, in line 235-236: That was redundant. Added a remark about malformed types.
https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:175: omitted. The rationale for this change is that a function type where a ... allows names of positional parameters to be omitted. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:184: The modification of the rule for the nonterminal `type` may cause parsing May? Does it or does it not? (I don't know, but we should figure that out, not leave it open). https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:195: lookahead. However, if a "diet parsing" strategy is used where the token Why unlimited? Isn't 2 tokens look-ahead enough? Drop "diet parsing" as a concept, it's not generally recognized (and if it is, it means something else that what is used here). Just "parsing strategy". https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:89: a left parenthesis (in short, we are "looking at `< .. >(`"). I understand the lure of the O(1) shortcut (given that tokenization has found the matching ">" already), but it's not something I want to enshrine into the language specification. First of all because it's too stict. It disallows syntax that would otherwise be valid by choosing a disambiguation strategy early, before it has even detected an ambiguity. We will allow more valid programs by not enforcing that choice. Second, this it might be easily implementable in the current parsers because of some design choices they have made, but basing the language design on a particular implementation strategy risks locking all future implementations to the same strategy, even if a better one is found. (See JavaScript RegExp - it's (near-)impossible to implement the correct semantics using a state-based RegExp engine because it really is a description of a specific implementation algorithm). Third, even if the implementations already contains the infrastructure for this optimization, the specification does not. In order to *specify* this behavior, we need to actually define the data-structure that the parsers build during tokenization, specify how it's updated and what it's state is at any point during parsing, and how it's used in this particular rule. That's new complexity in the specification, and if the implementations have chosen slightly different data structures (say, what happens for particular sequences of unmatched braces), then we might end up enforcing one implementation's choices on another implementation. Finally, it's unnecessary. Since implementations have managed without this bail-out, it doesn't seem necessary. They can still use the available look-ahead to detect the cases where there can't possibly be an ambiguity. For the (likely few) remaining cases, they will have to do some sort of look-ahead, but checking for types first seems like the safest bet - type expressions are simpler than general expressions, so it's likely to be rejected early if it isn't actually type expressions, and if succeeds matching as type expressions up to the ">(" then it will be a valid generic invocation - if it is valid at all. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:91: This implies that an expression like `foo(a<b,2>(d))` will be rejected I think it is possible - because the preferred interpretation is also the one with the simplest grammar. Trying to parse the content between "<" and ">(" as type expressions is unlikely to spend a lot of time before failing, so if we use the proposed look-ahead as a hint instead of a rule, I think performance will still be fine. Parsing isn't the most expensive part of reading in a program anyway, I think I've heard an estimate of it being ten times faster than tokenization. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:127: instance and type arguments for the constructor itself. It is possible that The primary reason we will not have generic getters and setters is that it breaks the getter/setter-field symmetry. I can't see any possible way to make a generic field. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:201: the value yielded by an evaluation of `dynamic`, following the treatment of Yes "incorrect". Read what I mean :) I agree that we should do something, and it's either "reserved word", "exported by dart:core", "implicitly in the scope of every library" (which is almost the same as "exported by dart:core" except that you can't hide it - but it's also completely new and needs its own specification). https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:211: program with a different runtime semantics for these expressions. The I'm not saying that compilers can't choose to implement an extension to the Dart language that allows constructs that are not valid Dart. They can do anything, including rejecting valid Dart programs, they're just not *Dart* compilers then (otherwise "perl" is a really crappy Dart compiler for a very extended very-subset of Dart :) I'm saying that the Dart language specification shouldn't mention it because it's, by definition, outside the scope of the Dart language. I cannot help reading the original text as a tacit endorsement of such a specific language extension, and that's not the job of the language specification. It should just say that it's a compile-time error, and then its job is done. It's like the traffic ordinance saying that "You are not allowed to cross at a pedestrian crossing when the light is red. Pedstrians are obviously free to do so anyway." Sure they are, they're just breaking the law, but that's not impossible, just illegal. A Dart compiler can extend the language, it's just not a Dart compiler any more, and if you develop your program using it, it might end up not being a Dart program. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:90: **generic function invocation**. Still disagree that this is what we want to specify. Unless you can come up with a short and clear way to introduce the notion of "balanced pair of angle brackets" to the specification, so that it captures exactly this case and avoids all the silly cases like "if (x<something) print(y >(a+b))", and doing that at a time when we haven't parsed anything yet, then I don't want that complication enshrined in the specification. Implementations can use it as a quick test for whether to even try parsing as a generic invocation, or they can do something even smarter that we haven't thought of yet. This is a premature optimization and lock-in of a specific optimization strategy. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:130: extension. This is correct. It is only the constructor restriction, and we could say that. Since the generic constructor case is actually something we are considering, I think the statement has value. If we weren't actively considering it, I'd drop the sentence entirely (being too speculative doesn't help anybody). https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:208: that (1) this allows more programs using generic methods to be compiled, Why don't we do that everywhere then, if that is an advantage? https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:211: program, so the expression must fail if it is ever evaluated. I still don't like this. Either it's a compile-time error or it's a runtime error. We should specify which one, and tests should be able to confirm that our implementations implement the specified behavior. Allowing dart2js to not follow the specification should be considered a bug (even if it's one we don't plan to fix), not a feature. When they shift to the common front end, it'll likely be an error anyway.
floitsch@google.com changed reviewers: + floitsch@google.com
DBC. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:3: **Status**: Under implementation. Implemented.
Review response (1), plus a bit more (2). (1) The only controversial thing would be the dynamic semantics of `e is T`/`e is! T` where `T` is a generic function type parameter. PTAL. (2) The generic function type alias text was updated to reflect the rule that Leaf reminded us of (generic function types cannot be type parameter bounds, nor actual type arguments). A couple of things were clarified, too. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:3: **Status**: Under implementation. On 2017/06/01 09:28:02, floitsch wrote: > Implemented. Done. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:90: **generic function invocation**. On 2017/06/01 07:57:00, Lasse Reichstein Nielsen wrote: > Still disagree that this is what we want to specify. > > Unless you can come up with a short and clear way to introduce the notion of > "balanced pair of angle brackets" to the specification, so that it captures > exactly this case and avoids all the silly cases like "if (x<something) print(y > >(a+b))", and doing that at a time when we haven't parsed anything yet, then I > don't want that complication enshrined in the specification. > > Implementations can use it as a quick test for whether to even try parsing as a > generic invocation, or they can do something even smarter that we haven't > thought of yet. This is a premature optimization and lock-in of a specific > optimization strategy. Agreed, it's not pretty. I thought we'd need it in order to ensure that the language is well-defined syntactically, but the `**favor**` part will actually do that. However, we could simply leave out the part that talks about how to ensure that this choice is made. This means that we may promise too much (and parsers won't actually implement the specification), but it's useful for developers that they can just think of the ambiguity and the "abstract" resolution, and then it's up to the implementations to do it right. Presumably, we can get it "almost right" with good performance, and nobody would ever know. ;-) Deleted the last sentence. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:130: extension. On 2017/06/01 07:57:00, Lasse Reichstein Nielsen wrote: > This is correct. It is only the constructor restriction, and we could say that. > > Since the generic constructor case is actually something we are considering, I > think the statement has value. If we weren't actively considering it, I'd drop > the sentence entirely (being too speculative doesn't help anybody). For setters and operators it isn't impossible. Who knows whether we might want it?: class C { int x; void operator []=<T>(int Function(T) f, T value) => x = f(value); List<T> operator []<T>(T Function() f) => [f()]; } main() { C c = new C(); c[(s) => s.length] = "Hello"; print(c[() => "Hello"]); // Explicit args are weird for `a +<MyType> b`, but [] might work: c<String>[(s) => s.length] = "Hello"; print(c<String>[() => "Hello"]); } Nevertheless, I just mention constructors in the new text. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:208: that (1) this allows more programs using generic methods to be compiled, On 2017/06/01 07:57:00, Lasse Reichstein Nielsen wrote: > Why don't we do that everywhere then, if that is an advantage? Because the vm folks thought it would be too much trouble to implement that. https://codereview.chromium.org/2841483003/diff/60001/docs/language/informal/... docs/language/informal/generic-method-syntax.md:211: program, so the expression must fail if it is ever evaluated. On 2017/06/01 07:57:00, Lasse Reichstein Nielsen wrote: > I still don't like this. > Either it's a compile-time error or it's a runtime error. But it is a compile-time error, and it is specified as such. > We should specify which one, and tests should be able to confirm that our > implementations implement the specified behavior. That was my preferred approach, too, a while back. But we agreed that implementations are allowed to "do whatever they want" with programs that incur compile-time errors (such that developers can experiment with their software during development), including giving them a run-time semantics. This is what dart2js does. > Allowing dart2js to not follow the specification should be > considered a bug Again, that was my argument a while back, but I was overruled: Implementations cannot give a semantics to erroneous programs unless the violate the specification in this manner. Wasn't my idea, but I'm following the approach that had the support of the majority. > (even if it's one we don't plan to fix), not a feature. When they shift to the > common front end, it'll likely be an error anyway. Why wouldn't they just get the reified semantics that we have in mind anyway?
https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:7: one available today, such that it also covers generic function types. The Consider using "generic-function types" or "types of generic functions" t avoid ambiguity. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:16: declaration itself, as well as in the declared type. This feature also > Type parameterization may occur in the declaration itself, as well as in the declared type. I'm not sure I understand that. I think it's being too "generic" in how it's talking about things. :) Is it saying that the typedef can be generic (parameterized by type), and the function type itself can be the type of a generic function (the function type is not parmeterized, it is the type of a generic function, big difference :). Consider removing the sentence and say that this allows making an alias for a the types of generic functions. The typedef-parameterization is not new, so you don't need to mention it. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:63: name, such that no type expressions will silently change meaning.* This is all speculative, not descriptive, so it doesn't help anybody who wants to implement the feature. Either remove it or move it to separate section at the end where it doesn't obscure the actual specification. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:65: There is a difference between declaring a generic function type and here maybe say "between declaring the type of a generic function and declaring a typedef ...." https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:70: function: It accepts a type argument at compile time and returns a type, "type-level function" uses "function" in a too-overloaded way - this is a document about function types, let's do function-type-functions as well, then we're sure to have lost all readers. That is: No meta! https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:83: Here, we have declared two equivalent parameterized typedefs `I` and `J`, Here -> In this example (just for explicitness) https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:143: typeWithoutFunction: // NEW A positive description is usually easier to work with than a negative description. Being "without function" doesn't say what it really is. That said, I can't find a good word to cover it (because it may contain function types anyway, just not the function-type syntax). https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:186: ambiguities. We intend to handle them by the following disambiguation rule intend to handle -> handle This is a specification (even if it's an informal one) so its text is normative. Readers can't use "intend" for anything, they need to know what to do or not do. IOW: Do, or do not, there is no "intend". https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:196: lookahead. However, if a "diet parsing" strategy is used where the token Drop "diet parsing", just say "parsing strategy". "Diet parsing" is not a general term, and where we use it, it doesn't mean just the brace-matching. It's more precisely described as parsing method signatures without parsing the bodies, but how that's done isn't defined. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:213: `SomeIdentifier<...> Function(...` where the angle brackets are balanced).* It's not clear to me where we allow parsing Function( as a function type. Is it only where a type may occur, or everywhere? How do we know where a type may occur? - start of declaration (type annotation) - start of parameter (type annotation) - after is/as/on - as type argument Is that it? This still sounds wishy-washy to me. Are you defining or describing things here? (Is it nominative or descriptive?) Are you just giving hints to parsers? How about just: - The grammar is changed as above. - The new grammar is ambiguous. Example: (pick a good example) - In any case where `Function(` and `Function<` occurs where it could be the prefix of a functionType production, it must parsed as a type. - This disambiguates all ambiguities introduced by the new grammar. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:241: Note that in this case the `typeParameters` are present, not optional. This Drop the "not optional". Here you are giving a concrete example (even if it's still abstract), not a pattern. Optionality only applies in patterns, so you are comparing a concrete example with a pattern, which is a difference in kind, and why I started out reading this as a pattern as well. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:247: `functionType`. I'm not sure it's worth it to avoid adding a typedef scope to typedefs without type parameters, it'll just be empty anyway and make no difference. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:277: a typedef has an associated `functionType` which depends directly or Define "depends". I guess it's actually pretty hard, it really means that the transitive closure of type declarations used by N must not refer to N. How about: class C implements List<N> {} class D extends C; typedef N = D Function(D); I'd say that it "depends on" N. How about: class C { N foo(N x) => x; } class D extends C; typedef N = D Function(D); Does that depend on N? Probably not, because the nominative type C is independent of N. (So, this is an unclear definition, but probably no more unclear than other similar dependency requirements we already have in the language). https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:283: an instantiation *F<T1..Tk>* of a parameterized typedef is mal-bounded. Is it? We allow super-bounded generic types in some cases now, so should we allow super-bounded function types (or does that not make sense)? https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:295: declarations of entities of kind ` * -> * ` cannot be curried.* Is this important? Does it affect implementation, or is it just commentary? (That is: I got lost reading this, and I didn't follow the link. Expect other readers to do the same. If this is important for implementation in any way, it needs to be rephrased to be actionable.)
https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:8: option) in the virtual machine and the analyzer, as well as in `dart2js`. Then mentioning dart2js and the flag is irrelevant. Just drop "which has been implemented .... " and the "by default". Just: This feature is available since SDK version 1.21. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:19: In this document, the word **routine** will be used when referring to "Routine" is usually a synonym for "procedure", not "function", which can be confusing since all Dart's callables are functions, not procedures. Read: It is confusing. Be aware that "operator+" is a method, but this does not apply to operators, so maybe modify the following to "which can be a non-operator method, ..." https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:21: a function literal expression. This matches values with syntax ("function" vs "expression"). Maybe add "declaration" to the method and functions so they are of the same kind as the "literal expression". Is "routine" referring to syntax or semantics? https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:27: depending on the context. No type checking takes place at usages of a method Evaluation only applies to expressions, so evaluating the type parameter always does the same thing (whatever the identifier expression does in that case). Consider changing "evaluations ... " to "uses of a routine type parameter ..." https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:89: This implies that an expression like `foo(a<b,2>(d))` will be rejected Nope, not any more. Remove this paragraph. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:93: correctly as an invocation of `foo` with two arguments. The breaking change is that: foo(a<b,c>(d)) will change behavior, because it *is* syntactically valid as a call with a generic function invocation, and it used to only be valid as a call with two relational expression. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:95: The **reason** why the generic function invocation is favored over the Consider dropping "why". It feels redundant. Or rewrite it as: We chose to favor the generic function invocation because ... https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:112: "diet" parsing is used and various kinds of bracket tokens are matched up drop "diet". Or: if a parsing strategy is used that matches bracket tokens during lexical analysis, ...
Review response concerning generic-function-type-alias.md. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:7: one available today, such that it also covers generic function types. The On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > Consider using "generic-function types" or "types of generic functions" t avoid > ambiguity. Added a paragraph making the distinction between the two groupings "(generic function) type" and "generic (function type)", and explicitly mentioning `generic-function type`. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:16: declaration itself, as well as in the declared type. This feature also On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > > Type parameterization may occur in the declaration itself, as well as in the > declared type. > > I'm not sure I understand that. I think it's being too "generic" in how it's > talking about things. :) > > Is it saying that the typedef can be generic (parameterized by type), and the > function type itself can be the type of a generic function (the function type is > not parmeterized, it is the type of a generic function, big difference :). Exactly. > Consider removing the sentence and say that this allows making an alias for a > the types of generic functions. The typedef-parameterization is not new, so you > don't need to mention it. I added references to the terminology introduced in the new paragraph which at least creates a connection to known terminology above, and to examples given later. I think it makes sense to mention parameterized typedefs even though the old syntax also supported that notion, because `typedef F<X> = ..` is new syntax. Besides, I'm sure lots of readers could be a little bit confused about the meaning of the old syntax, e.g., `typedef void f<T>(T t);`. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:63: name, such that no type expressions will silently change meaning.* On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > This is all speculative, not descriptive, so it doesn't help anybody who wants > to implement the feature. Either remove it or move it to separate section at the > end where it doesn't obscure the actual specification. Deleted it. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:65: There is a difference between declaring a generic function type and On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > here maybe say "between declaring the type of a generic function and declaring a > typedef ...." With the changes earlier in this document, the phrase 'generic function type' has been introduced explicitly. I suppose we'll just create confusion by using a different phrase here to mean exactly the same thing. The explanation that spells out exactly what `generic function type` means is right here in line 68++, anyway. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:70: function: It accepts a type argument at compile time and returns a type, On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > "type-level function" uses "function" in a too-overloaded way - this is a > document about function types, let's do function-type-functions as well, then > we're sure to have lost all readers. > That is: No meta! But it _is_ a type-level function, and a bunch of people (e.g., anyone who has been looking at Scala typing) would consider that concept to be natural. However, I replaced the phrase 'type-level function' by 'compile-time mapping from types to types', hoping that this would carry enough context-setting information to make it more accessible. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:83: Here, we have declared two equivalent parameterized typedefs `I` and `J`, On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > Here -> In this example > (just for explicitness) Done. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:143: typeWithoutFunction: // NEW On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > A positive description is usually easier to work with than a negative > description. Being "without function" doesn't say what it really is. > That said, I can't find a good word to cover it (because it may contain function > types anyway, just not the function-type syntax). > Right, it is "any type which is not syntactically a function type". The name of this non-terminal was modeled on several existing non-terminals named '..WithoutCascade'. No changes applied for now. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:186: ambiguities. We intend to handle them by the following disambiguation rule On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > intend to handle -> handle > > This is a specification (even if it's an informal one) so its text is normative. > Readers can't use "intend" for anything, they need to know what to do or not do. > > IOW: Do, or do not, there is no "intend". Right, `intend` must go. But I think the main issue here is that we just decided to drop the heuristic in the generic method syntax document, going for the idealistic "disambiguation favors a generic invocation" and ignoring the consequences of that approach for the complexity of parsing. So maybe we should simply say that in case of ambiguity we favor a parse that makes `Function` part of a function type? This would be an extension of the current rule, because "commit to parsing a function type upon having seen .." cannot ever do anything other than forcing exactly the same disambiguation in some cases (and possibly not all of them). I have no idea how difficult it would be to satisfy that specification in a parser, though. Right now I've kept the heuristic and just deleted the `intend` part. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:196: lookahead. However, if a "diet parsing" strategy is used where the token On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > Drop "diet parsing", just say "parsing strategy". > "Diet parsing" is not a general term, and where we use it, it doesn't mean just > the brace-matching. It's more precisely described as parsing method signatures > without parsing the bodies, but how that's done isn't defined. Done. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:213: `SomeIdentifier<...> Function(...` where the angle brackets are balanced).* On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > It's not clear to me where we allow parsing Function( as a function type. > Is it only where a type may occur, or everywhere? The words `the tokens starting at L may be a \`type\` or some other construct` are intended to specify that this rule only applies when the current location can be a `type` (that is, there exists a suffix to the input that we have already read such that the complete input is syntactically correct and the construct starting at L is a `type`). > How do we know where a type may occur? We're not interested in all locations where a type may occur, only the locations where it may occur, and something else may also occur. (With the idealistic approach where we just say what's "favored" in case of an ambiguity, we will need to know whether the input can be such that >1 parse tree is possible; with a heuristic we will actually allow the parser to make a commitment to parsing a `type`, even though there may be cases where that leads to a parse error and a different choice would lead to a successful parse). So---when may we have a type and something else? Ideally, we consider all possible libraries (i.e., complete inputs) which begin with the input that we have already read, and which are syntactically correct Dart libraries. Then we consider all parse trees admitted by the grammar for those inputs. If there exists one such parse tree where the leaf at location L is the left-most leaf of a subtree of the parse tree whose root is `type`, then "a `type` may occur at L". In practice, if we use a recursive descent parser (as we do) and it is complete (so, if there is a parse tree on a given input, the parser will find that parse tree: presumably we do have that property) then the parser will need to make a choice between parsing a `type` and parsing something else at L, which means that it will call either `parseType`, `parseX1`, .., or `parseXn`, for some other non-terminals or terminals X1..Xn. So in this case we know that we will _only_ need to use this disambiguation rule in a situation where we need to make such a choice. Isn't that reasonably well-understood? > - start of declaration (type annotation) > - start of parameter (type annotation) > - after is/as/on > - as type argument > Is that it? Sounds plausible, but even without a complete list I think it's well-defined. > This still sounds wishy-washy to me. > > Are you defining or describing things here? (Is it nominative or descriptive?) > Are you just giving hints to parsers? It is a specification of a disambiguation heuristic that parsers may use. Hence, it is a limit on the set of syntactically correct Dart libraries: If a library gives rise to a parsing failure with a Dart parser which uses this heuristic, it is a problem with the library, not a bug in the parser. We may even say that parsers _must_ use this heuristic, such that all libraries which _might_ incur this parsing failure _will_ indeed have this parsing failure. > How about just: > - The grammar is changed as above. > - The new grammar is ambiguous. Example: (pick a good example) > - In any case where `Function(` and `Function<` occurs where it could be the > prefix of a functionType production, it must parsed as a type. > - This disambiguates all ambiguities introduced by the new grammar. OK, that was exactly what I suggested a few comments ago, based on a similar change in the other document of this CL. The problem is that we may not be able to implement that (without excessive performance hits). ... WDYT? ... https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:241: Note that in this case the `typeParameters` are present, not optional. This On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > Drop the "not optional". Here you are giving a concrete example (even if it's > still abstract), not a pattern. Optionality only applies in patterns, so you are > comparing a concrete example with a pattern, which is a difference in kind, and > why I started out reading this as a pattern as well. Dropped `optional`, that's clearly better. By the way, this _is_ a pattern. It allows for the construction of a subset of the strings derivable from the non-terminal `typeAlias` (and it is not equal to the set of strings derivable from any existing non-terminal, so I couldn't just use such a non-terminal). The following text is applicable to all of those derivatives, so it does matter that it's not just an example. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:247: `functionType`. On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > I'm not sure it's worth it to avoid adding a typedef scope to typedefs without > type parameters, it'll just be empty anyway and make no difference. Right. It'll be a small change. I'm not doing it now, let's decide on that tomorrow IRL. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:277: a typedef has an associated `functionType` which depends directly or On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > Define "depends". > I guess it's actually pretty hard, it really means that the transitive closure > of type declarations used by N must not refer to N. This is concerned with static analysis, and dependencies can only arise when a compile-time lookup operation resolves a given name application N1 to a specific declaration D1, which in turn establishes a dependency edge from N1 to D1; edges coming from the body of a declaration D2 are considered to be edges from D2 to other declarations. Now, it is a compile-time error if a typedef declaration D is such that the static dependency graph has a cycle whose nodes are all typedefs. > How about: > > class C implements List<N> {} > class D extends C; Can't do that. But ok. ;-) > typedef N = D Function(D); > > I'd say that it "depends on" N. With the 'all typedefs' clause that would be allowed. Consider this variant: class C implements List<D Function(D)> {} class D extends C {} This is obviously "the same thing", but there is nothing stopping us from having such a setup. So I wouldn't expect problems caused by allowing the declarations that include `typedef N` (i.e., your example). A main point would be that we don't want to go beyond type aliases, so in particular a `typedef` cannot be allowed to be recursive. This would be prevented in a very direct manner by requiring that there are no dependency cycles _of_typedefs_, and I suspect that other cycles are benign. Can you think of any exceptions? > How about: > > class C { > N foo(N x) => x; > } > class D extends C; > typedef N = D Function(D); Same situation, same response: No problem. > Does that depend on N? Probably not, because the nominative type C is > independent of N. > > (So, this is an unclear definition, but probably no more unclear than other > similar dependency requirements we already have in the language). That's possible, but it shouldn't be hard to specify the "cycle of typedefs" requirement. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:283: an instantiation *F<T1..Tk>* of a parameterized typedef is mal-bounded. On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > Is it? > We allow super-bounded generic types in some cases now, so should we allow > super-bounded function types (or does that not make sense)? Aha?! I supported super-bounded types and still do, so that sounds good, but I had no idea that they had been introduced. When was that? Anyway, we could also decide that parameterized typedefs don't have bounds at all, the constraints on type arguments are derived from the body (so an instantiation *F<T1..Tk>* is malbounded if and only if the resulting body from that instantiation contains a malbounded type). That would be "ugly" in the sense that the typedef as such is de-emphasized as an abstraction (it leaks), but on the other hand it would take away the requirement to tease out some working bounds which may in some cases be an intersection type which can't be specified). Another difficulty is that we may have cases where we need a lower bound (when function types have contravariant argument types, i.e., in strong mode): typedef F<X> = void Function(X); class C<Y extends F<num>> {} typedef G<X super num> = C<F<X>> Function(); .. except that we can't say "X super num". It's not obvious to me how we can handle bounds on parameterized typedefs in a good way. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:295: declarations of entities of kind ` * -> * ` cannot be curried.* On 2017/07/11 11:13:53, Lasse Reichstein Nielsen wrote: > Is this important? Does it affect implementation, or is it just commentary? > (That is: I got lost reading this, and I didn't follow the link. Expect other > readers to do the same. If this is important for implementation in any way, it > needs to be rephrased to be actionable.) It is intended to be helpful for people who know "kind systems" from languages like Haskell or Scala or from theoretical calculi. I've changed the wording to make it more obvious that this particular sentence is intended for those readers who already know kind systems. Since the whole paragraph is marked as rationale (*..*) it is already indicated that it is optional reading.
Looks partially good to me (the function alias one, with a few comments). https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:70: function: It accepts a type argument at compile time and returns a type, Let's not write something that can only be understood by "a bunch of people". :) People who understand meta-functions, there are dozens of us! https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:186: ambiguities. We intend to handle them by the following disambiguation rule As discussed offline, there is a difference between this syntax and the generic function call syntax — mainly that this syntax is very local so the user can easily determine whether the rule applies or not, where the generic function syntax requires reasoning about far-away matching braces too. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:213: `SomeIdentifier<...> Function(...` where the angle brackets are balanced).* On 2017/07/11 15:50:05, eernst wrote: > On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > > How about just: > > - The grammar is changed as above. > > - The new grammar is ambiguous. Example: (pick a good example) > > - In any case where `Function(` and `Function<` occurs where it could be the > > prefix of a functionType production, it must parsed as a type. > > - This disambiguates all ambiguities introduced by the new grammar. > > OK, that was exactly what I suggested a few comments ago, based on a similar > change in the other document of this CL. The problem is that we may not be able > to implement that (without excessive performance hits). > > ... WDYT? ... That's actually not what I'm saying - this does use only the prefix to determine whether to commit to the `Function(` or `Function<` being function-type syntax or an identifier followed by another token. It does not use later tokens to make the determination, which means that later tokens can be invalid for the chosen parsing, even if it would be valid for another parsing of the same input. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:283: an instantiation *F<T1..Tk>* of a parameterized typedef is mal-bounded. Agree that it is a problem, and as discussed it doesn't stop at super-bounds. We can probably cause multiple constraints on a type variable that would require any or all of union, intersection and super-bounded types to express. We need to figure out whether the `G` declaration above is allowed without the "super num", and if so, when any actual error is reported. https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:73: mapping from types to types: It accepts a type argument at compile time and It's not entirely compile-time. Some types are only known at runtime, so the actual mapping has to happen there: typedef F<T> = Function(T); class C<T> { test(Function f) => f is F<T>; } Here the parameterization of `F<T>` can only happen after an instance of C has been created, so it's necessarily at runtime. https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:194: declaration), the parser can commit to parsing a `type` if it can commit -> must commit It can't not commit, or we'll have nondeterministic parsing :)
Review response on generic-method-syntax.md. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... File docs/language/informal/generic-method-syntax.md (right): https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:8: option) in the virtual machine and the analyzer, as well as in `dart2js`. On 2017/07/11 11:41:37, Lasse Reichstein Nielsen wrote: > Then mentioning dart2js and the flag is irrelevant. Just drop "which has been > implemented .... " and the "by default". > Just: > This feature is available since SDK version 1.21. Done. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:19: In this document, the word **routine** will be used when referring to On 2017/07/11 11:41:36, Lasse Reichstein Nielsen wrote: > "Routine" is usually a synonym for "procedure", not "function", which can be > confusing since all Dart's callables are functions, not procedures. > Read: It is confusing. > > Be aware that "operator+" is a method, but this does not apply to operators, so > maybe modify the following to "which can be a non-operator method, ..." The intention behind using `routine` was that this is a word which denotes some behavioral abstraction, and it is not already used in Dart to mean something else. We can't just say `function` because that covers too much, `method` covers too little and then some (operators), etc, so we do need a new word to denote this new set of things. I don't have a nicer choice, so I've kept it unchanged, but I admit that it is somewhat confusing. Operator issue: Fixed. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:21: a function literal expression. On 2017/07/11 11:41:36, Lasse Reichstein Nielsen wrote: > This matches values with syntax ("function" vs "expression"). > Maybe add "declaration" to the method and functions so they are of the same kind > as the "literal expression". Adjusted to make it obvious that these are all syntactic constructs. The semantics is implied: These syntactic constructs correspond to semantic entities with a dynamic meaning (and a representation at run time, in typical implementations), and `a routine` may also denote those semantic entities. > Is "routine" referring to syntax or semantics? Both, but I think it's reasonably obvious from the context which one applies in each case. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:27: depending on the context. No type checking takes place at usages of a method On 2017/07/11 11:41:36, Lasse Reichstein Nielsen wrote: > Evaluation only applies to expressions, so evaluating the type parameter always > does the same thing (whatever the identifier expression does in that case). Well, it's about expressions, like `foo<T>() => T;`, and types, like `e is List<T>` or `foo<T>() => List<T>;` where `T` is a routine type parameter. If we refuse to say that "`T` gets evaluated" for the latter then it's difficult to explain how the representation of `List<T>` could include a representation of `T`. So what do we call it? Calling it `uses` seems to be benign, anyway, so I'm using that. > Consider changing "evaluations ... " to "uses of a routine type parameter ..." https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:89: This implies that an expression like `foo(a<b,2>(d))` will be rejected On 2017/07/11 11:41:36, Lasse Reichstein Nielsen wrote: > Nope, not any more. Remove this paragraph. Oops, missed that. Done! https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:93: correctly as an invocation of `foo` with two arguments. On 2017/07/11 11:41:36, Lasse Reichstein Nielsen wrote: > The breaking change is that: > foo(a<b,c>(d)) > will change behavior, because it *is* syntactically valid as a call with a > generic function invocation, and it used to only be valid as a call with two > relational expression. Added a short paragraph saying that. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:95: The **reason** why the generic function invocation is favored over the On 2017/07/11 11:41:36, Lasse Reichstein Nielsen wrote: > Consider dropping "why". It feels redundant. > Or rewrite it as: > We chose to favor the generic function invocation because ... Done. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-method-syntax.md:112: "diet" parsing is used and various kinds of bracket tokens are matched up On 2017/07/11 11:41:36, Lasse Reichstein Nielsen wrote: > drop "diet". > Or: > if a parsing strategy is used that matches bracket tokens during lexical > analysis, ... Done.
More review response, including a couple of old comments that hadn't been addressed, and many new ones. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:175: omitted. The rationale for this change is that a function type where a On 2017/06/01 07:56:59, Lasse Reichstein Nielsen wrote: > ... allows names of positional parameters to be omitted. Done. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:184: The modification of the rule for the nonterminal `type` may cause parsing On 2017/06/01 07:56:59, Lasse Reichstein Nielsen wrote: > May? Does it or does it not? > (I don't know, but we should figure that out, not leave it open). Done. https://codereview.chromium.org/2841483003/diff/40001/docs/language/informal/... docs/language/informal/generic-function-type-alias.md:195: lookahead. However, if a "diet parsing" strategy is used where the token On 2017/06/01 07:56:59, Lasse Reichstein Nielsen wrote: > Why unlimited? Isn't 2 tokens look-ahead enough? > > Drop "diet parsing" as a concept, it's not generally recognized (and if it is, > it means something else that what is used here). Just "parsing strategy". > Done. The lookahead has no finite limit: In `foo(a<b, ..., x>(y))`, there is no limit to the number of "possible type arguments" represented by `...`, and they may all be parseable as types and also as expressions. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:213: `SomeIdentifier<...> Function(...` where the angle brackets are balanced).* On 2017/07/12 12:08:06, Lasse Reichstein Nielsen wrote: > On 2017/07/11 15:50:05, eernst wrote: > > On 2017/07/11 11:13:52, Lasse Reichstein Nielsen wrote: > > > > How about just: > > > - The grammar is changed as above. > > > - The new grammar is ambiguous. Example: (pick a good example) > > > - In any case where `Function(` and `Function<` occurs where it could be the > > > prefix of a functionType production, it must parsed as a type. > > > - This disambiguates all ambiguities introduced by the new grammar. > > > > OK, that was exactly what I suggested a few comments ago, based on a similar > > change in the other document of this CL. The problem is that we may not be > able > > to implement that (without excessive performance hits). > > > > ... WDYT? ... > > That's actually not what I'm saying - this does use only the prefix to determine > whether to commit to the `Function(` or `Function<` being function-type syntax > or an identifier followed by another token. It does not use later tokens to make > the determination, which means that later tokens can be invalid for the chosen > parsing, even if it would be valid for another parsing of the same input. > As discussed IRL, there is no finite bound on the `type` part which may occur before `Function`, and we do not have a simple rule which will eliminate the need to parse the `type` in total before we encounter `Function`, `(`/'<` and make a decision. But I think we agreed that it is not a problem, in practice those `type` expressions are not going to be large, and the problem does not lend itself to exponential blowup. https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:283: an instantiation *F<T1..Tk>* of a parameterized typedef is mal-bounded. On 2017/07/12 12:08:06, Lasse Reichstein Nielsen wrote: > Agree that it is a problem, and as discussed it doesn't stop at super-bounds. We > can probably cause multiple constraints on a type variable that would require > any or all of union, intersection and super-bounded types to express. > > We need to figure out whether the `G` declaration above is allowed without the > "super num", and if so, when any actual error is reported. I'd suggest that we use the current behavior of `dartanalyzer --strong`, which is to require that the typedef has declared bounds which force the body to be well-bounded. The example `G` will then be impossible to compile, but the `typedef` construct as such will be a more well-rounded abstraction. To make this happen, I adjusted the wording to say `well-bounded`, about the body.
Yet more reviewing response. https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:73: mapping from types to types: It accepts a type argument at compile time and On 2017/07/12 12:08:07, Lasse Reichstein Nielsen wrote: > It's not entirely compile-time. Some types are only known at runtime, so the > actual mapping has to happen there: > > typedef F<T> = Function(T); > class C<T> { > test(Function f) => f is F<T>; > } > > Here the parameterization of `F<T>` can only happen after an instance of C has > been created, so it's necessarily at runtime. Wouldn't it be a reasonable semantics for that `C` to expand the typedef and then rely on the dynamic semantics of the expansion? In that case the typedef doesn't have a dynamic semantics per se. https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:194: declaration), the parser can commit to parsing a `type` if it On 2017/07/12 12:08:07, Lasse Reichstein Nielsen wrote: > can commit -> must commit > > It can't not commit, or we'll have nondeterministic parsing :) Done.
https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/120001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:213: `SomeIdentifier<...> Function(...` where the angle brackets are balanced).* Agree. It's worth recognizing that SomeIdentifier<...> appears where a type is possible, so it's not inside a place that allows top-level comma-separated expressions. I.e., SomeIdentifier<foo, bar> must be a type, and SomeIdentifier<foo> must be a type. Basically, we can commit SomeIdentifier< to being a type at the first "," or ">", before even reaching Function, which means that *if* there is a matching ">" for the "SomeIdentifier<", then it is a type, not a comparison (if there is no comma, it's not a valid expression, if there is a comma, it's not separating expressions, so the <...> must be a type argument). https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:73: mapping from types to types: It accepts a type argument at compile time and That's also a reasonable interpretation, but then it's not a mapping from types to types, but from type-expressions to type-expressions (it's a syntax-level macro, not a type-level function). The type parameter "T" is not a *type* at compile-time, only at runtime, but it is a type-expression.
"Do we have type-level computations?" https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:73: mapping from types to types: It accepts a type argument at compile time and First, the reason why I emphasized `type-level function` from the very beginning, and switched to `compile-time mapping from types to types` later, is that I think this gives the reader the clearest possible distinction between those two concepts. A parameterized typedef is a compile-time only concept, there is no need to leave any traces of their existence at run time. A generic function needs to exist at run time, and it needs to have properties that reflect its ability to accept both type and value arguments. Don't you think that compile/run-time distinction is helpful? On 2017/07/13 06:57:17, Lasse Reichstein Nielsen wrote: > That's also a reasonable interpretation, but then it's not a mapping from types > to types, but from type-expressions to type-expressions (it's a syntax-level > macro, not a type-level function). The type parameter "T" is not a *type* at > compile-time, only at runtime, but it is a type-expression. That's actually a very tempting stance! (Types only exist at run-time, just like everything else, except syntax). But it wouldn't be hard at all to find (very serious) people who would insist that types _only_ exist at compile time, and if there is something similar to types at run time it would be "type tags", indicating that any given object has a type which is represented by that tag. I've got enough of that bug to disagree, we do not only have (1) run-time semantics, and (2) syntax. In particular, the language specification requires many different kinds of type-level computations to take place during static analysis. For instance, we may have a `class D<T> { List<T> get foo; }` in a program, and `myD.foo` could have type `List<List<String>>`, but the program does not have to contain `List<List<String>>` as syntax anywhere. So that's a type, and it's not syntax, and it's actually produced by a compile-time computation. Another example is that the body of `D` can use `T`, which is also a type because it derives from `type` and it conforms to the relevant static checks. It's a first class type because different instances of `D` may have different values for `T`, but that doesn't prevent static analysis checking usages of `T`. For the compile-time computations on types, `T` is a type just as much as `int` is a type, and it doesn't matter whether or not any particular type during these computations existed as syntax at any point in time.
lgtm https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... File docs/language/informal/generic-function-type-alias.md (right): https://codereview.chromium.org/2841483003/diff/140001/docs/language/informal... docs/language/informal/generic-function-type-alias.md:73: mapping from types to types: It accepts a type argument at compile time and The usual Java story is that types only exist at compile-time and it's "class" at runtime (hence `Foo.class`, of type `Class<Foo>` being the runtime reflection of the `Foo` type). They also say "variables have types, objects have classes". That's probably On the other hand, they do erasure, so a lot of things does not exist at runtime :) I think my opinion is that types exist both at compile-time and at runtime. At runtime they may be represented by "type tags", but that just means that compile-time types and runtime types are distinct, not that they are not both "types". We can do full type reasoning, subtype-checks, at runtime (through generics: bool isSubtype<A,B>=><A>[] is List<B>;) I think the distinction between compile-time and runtime is good, so my primary concern is that the mapping appears to be either syntactic or happen at runtime — because it works on type variables which are not types at compile-time. If we see type variables as types (that is "? extends T" is a type in the static type system), then I guess the explanation works. And again, this is explanatory, so maybe it can get away with not being absolutely correct as long as it provides a good way to think about things. So, LGTM.
Description was changed from ========== Added informal generic method syntax and generic function type specs. ========== to ========== Added informal generic method syntax and generic function type specs. R=lrn@google.com Committed: https://github.com/dart-lang/sdk/commit/5904409d50fa9b800d015faadbe49cb13e358644 ==========
Message was sent while issue was closed.
Committed patchset #12 (id:220001) manually as 5904409d50fa9b800d015faadbe49cb13e358644 (presubmit successful). |