Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(157)

Side by Side Diff: docs/language/informal/generic-function-type-alias.md

Issue 2841483003: Added informal generic method syntax and generic function type specs. (Closed)
Patch Set: Added wording on how to treat ty-vars in an `on` clause Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 # Feature: Generic Function Type Alias
2
3 **Status**: Implemented.
4
5 **This document** is an informal specification of a feature supporting the
6 definition of function type aliases using a more expressive syntax than the
7 one available today, such that it also covers generic function types. The
Lasse Reichstein Nielsen 2017/07/11 11:13:52 Consider using "generic-function types" or "types
eernst 2017/07/11 15:50:04 Added a paragraph making the distinction between t
8 feature also introduces syntax for specifying function types directly, such
9 that they can be used in type annotations etc. without going via a
10 `typedef`.
11
12 **This feature** introduces a new syntactic form of typedef declaration
13 which includes an identifier and a type, connecting the two with an equals
14 sign, `=`. The effect of such a declaration is that the name is declared to
15 be an alias for the type. Type parameterization may occur in the
16 declaration itself, as well as in the declared type. This feature also
Lasse Reichstein Nielsen 2017/07/11 11:13:53 I'm not sure I understand that. I think it's being
eernst 2017/07/11 15:50:04 Exactly.
17 introduces syntax for specifying function types directly, using a syntax
18 which is similar to the header of a function declaration.
19
20 The **motivation** for adding this feature is that it allows developers to
21 specify generic function types at all, and to specify function types
22 everywhere a type is expected. That includes type annotations, return types,
23 actual type arguments, and formal type parameter bounds. Currently there is
24 no way to specify a function type directly in these situations. Even in the
25 case where a function type *can* be specified (such as a type annotation for
26 a formal parameter) it may be useful for readability to declare a name as an
27 alias of a complex type, and use that name instead of the type.
28
29 ## Examples
30
31 Using the new syntax, a function type alias may be declared as follows:
32
33 ```dart
34 typedef F = List<T> Function<T>(T);
35 ```
36
37 This declares `F` to be the type of a function that accepts one type
38 parameter `T` and one value parameter of type `T` whose name is
39 unspecified, and returns a result of type `List<T>`. It is possible to use
40 the new syntax to declare function types that we can already declare using
41 the existing typedef declaration. For instance, `G` and `H` both declare
42 the same type:
43
44 ```dart
45 typedef G = List<int> Function(int); // New form.
46 typedef List<int> H(int i); // Old form.
47 ```
48
49 Note that the name of the parameter is required in the old form, but the
50 type may be omitted. In contrast, the type is required in the new form, but
51 the name may be omitted.
52
53 The reason for having two ways to express the same thing is that the new
54 form seamlessly covers non-generic functions as well as generic ones, and
55 developers might prefer to use the new form everywhere, for improved
56 readability.
57
58 *We may deprecate the old form after a while, or we may choose
59 to keep it, because it is more concise. We may even change the old form to
60 allow omitting the name and not the type when only one identifier is
61 specified, if this is not too much of a breaking change. As an intermediate
62 step we could change the old form to always require both the type and the
63 name, such that no type expressions will silently change meaning.*
Lasse Reichstein Nielsen 2017/07/11 11:13:53 This is all speculative, not descriptive, so it do
eernst 2017/07/11 15:50:04 Deleted it.
64
65 There is a difference between declaring a generic function type and
Lasse Reichstein Nielsen 2017/07/11 11:13:53 here maybe say "between declaring the type of a ge
eernst 2017/07/11 15:50:04 With the changes earlier in this document, the phr
66 declaring a typedef which takes a type argument. The former is a
67 declaration of a single type which describes a certain class of runtime
68 entities: Functions that are capable of accepting some type arguments as
69 well as some value arguments, both at runtime. The latter is a type-level
70 function: It accepts a type argument at compile time and returns a type,
Lasse Reichstein Nielsen 2017/07/11 11:13:53 "type-level function" uses "function" in a too-ove
eernst 2017/07/11 15:50:05 But it _is_ a type-level function, and a bunch of
Lasse Reichstein Nielsen 2017/07/12 12:08:06 Let's not write something that can only be underst
71 which may be used, say, as a type annotation. We use the phrase
72 *parameterized typedef* to refer to the latter. Dart has had support for
73 parameterized typedefs for a while, and the new syntax supports
74 parameterized typedefs as well. Here is an example of a parameterized
75 typedef, and a usage thereof:
76
77 ```dart
78 typedef I<T> = List<T> Function(T); // New form.
79 typedef List<T> J<T>(T t); // Old form.
80 I<int> myFunction(J<int> f) => f;
81 ```
82
83 Here, we have declared two equivalent parameterized typedefs `I` and `J`,
Lasse Reichstein Nielsen 2017/07/11 11:13:53 Here -> In this example (just for explicitness)
eernst 2017/07/11 15:50:05 Done.
84 and we have used an instantiation of each of them in the type annotations
85 on `myFunction`. Note that the type of `myFunction` does not include *any*
86 generic types, it is just a function that accepts an argument and returns a
87 result, both of which have a non-generic function type that we have
88 obtained by instantiating a parameterized typedef. The argument type might
89 as well have been declared using the traditional function signature syntax,
90 and the return type (and the argument type, by the way) might as well have
91 been declared using a regular, non-parameterized typedef:
92
93 ```dart
94 typedef List<int> K(int i); // Old form, non-generic.
95 K myFunction2(List<int> f(int i)) => f; // Same as myFunction.
96 ```
97
98 The new syntax allows for using the two kinds of type parameters together:
99
100 ```dart
101 typedef L<T> = List<T> Function<S>(S, {T Function(int, S) factory});
102 ```
103
104 This declares `L` to be a parameterized typedef; when instantiating `L`
105 with an actual type argument as in `L<String>`, it becomes the type of a
106 generic function that accepts a type argument `S` and two value arguments:
107 one required positional argument of type `S`, and one named optional
108 argument with name `factory` and type `String Function(int, S)`; finally,
109 it returns a value of type `List<String>`.
110
111 ## Syntax
112
113 The new form of `typedef` declaration uses the following syntax (there are
114 no deletions from the grammar; addition of a new rule or a new alternative
115 in a rule is marked with NEW and modified rules are marked CHANGED):
116
117 ```
118 typeAlias:
119 metadata 'typedef' typeAliasBody |
120 metadata 'typedef' identifier typeParameters? '=' functionType ';' // NEW
121 functionType: // NEW
122 returnType? 'Function' typeParameters? parameterTypeList
123 parameterTypeList: // NEW
124 '(' ')' |
125 '(' normalParameterTypes ','? ')' |
126 '(' normalParameterTypes ',' optionalParameterTypes ')' |
127 '(' optionalParameterTypes ')'
128 normalParameterTypes: // NEW
129 normalParameterType (',' normalParameterType)*
130 normalParameterType: // NEW
131 type | typedIdentifier
132 optionalParameterTypes: // NEW
133 optionalPositionalParameterTypes | namedParameterTypes
134 optionalPositionalParameterTypes: // NEW
135 '[' normalParameterTypes ','? ']'
136 namedParameterTypes: // NEW
137 '{' typedIdentifier (',' typedIdentifier)* ','? '}'
138 typedIdentifier: // NEW
139 type identifier
140 type: // CHANGED
141 typeWithoutFunction |
142 functionType
143 typeWithoutFunction: // NEW
Lasse Reichstein Nielsen 2017/07/11 11:13:52 A positive description is usually easier to work w
eernst 2017/07/11 15:50:05 Right, it is "any type which is not syntactically
144 typeName typeArguments?
145 typeWithoutFunctionList: // NEW
146 typeWithoutFunction (',' typeWithoutFunction)*
147 mixins: // CHANGED
148 'with' typeWithoutFunctionList
149 interfaces: // CHANGED
150 'implements' typeWithoutFunctionList
151 superclass: // CHANGED
152 'extends' typeWithoutFunction
153 mixinApplication: // CHANGED
154 typeWithoutFunction mixins interfaces?
155 newExpression: // CHANGED
156 'new' typeWithoutFunction ('.' identifier)? arguments
157 constObjectExpression: // CHANGED
158 'const' typeWithoutFunction ('.' identifier)? arguments
159 redirectingFactoryConstructorSignature: // CHANGED
160 'const'? 'factory' identifier ('.' identifier)?
161 formalParameterList '=' typeWithoutFunction ('.' identifier)?
162 ```
163
164 The syntax relies on treating `Function` as a fixed element in a function
165 type, similar to a keyword or a symbol (many languages use symbols like
166 `->` to mark function types).
167
168 *The rationale for using this form is that it makes a function type very
169 similar to the header in a declaration of a function with that type: Just
170 replace `Function` by the name of the function, and add missing parameter
171 names and default values.*
172
173 *The syntax differs from the existing function type syntax
174 (`functionSignature`) in that the existing syntax allows the type of a
175 parameter to be omitted, but the new syntax allows parameter names to be
176 omitted. The rationale for this change is that a function type where a
177 parameter has a specified name and no type is very likely to be a
178 mistake. For instance, `int Function(int)` should not be the type of a
179 function that accepts an argument named "int" of type `dynamic`, it should
180 specify `int` as the parameter type and allow the name to be
181 unspecified. It is still possible to opt in and specify the parameter name,
182 which may be useful as documentation, e.g., if several arguments have the
183 same type.*
184
185 The modification of the rule for the nonterminal `type` may cause parsing
186 ambiguities. We intend to handle them by the following disambiguation rule
Lasse Reichstein Nielsen 2017/07/11 11:13:53 intend to handle -> handle This is a specificatio
eernst 2017/07/11 15:50:04 Right, `intend` must go. But I think the main iss
Lasse Reichstein Nielsen 2017/07/12 12:08:06 As discussed offline, there is a difference betwee
187 in the parser: If the parser is at a location L where the tokens starting
188 at L may be a `type` or some other construct (e.g., in the body of a
189 method, when parsing something that may be a statement and may also be a
190 declaration), the parser can commit to parsing a type by detecting that it
191 is looking at the identifier `Function` followed by `<` or `(`, or that it
192 is looking at a type followed by the identifier `Function` followed by `<`
193 or `(`.
194
195 *Note that this disambiguation rule does require parsers to have unlimited
196 lookahead. However, if a "diet parsing" strategy is used where the token
Lasse Reichstein Nielsen 2017/07/11 11:13:53 Drop "diet parsing", just say "parsing strategy".
eernst 2017/07/11 15:50:04 Done.
197 stream already contains references from each opening bracket (such as `<`
198 or `(`) to the corresponding closing bracket then the decision can be
199 taken in a fixed number of steps: If the current token is `Function` then
200 check the immediate successor (`<` or `(` means yes, we are looking at
201 a `type`, everything else means no) and we're done; if the first token is
202 an `identifier` other than `Function` then we can check whether it is a
203 `qualified` by looking at no more than the two next tokens, and we may then
204 check whether the next token again is `<`; if it is not then we look for
205 `Function` and the token after that, and if it is `<` then look for the
206 corresponding `>` (we have now skipped a generic class type), and then
207 the successor to that token again must be `Function`, and we finally check
208 its successor (looking for `<` or `(` again). This skips over the
209 presumed type arguments to a generic class type without checking that they
210 are actually type arguments, but we conjecture that there are no
211 syntactically correct alternatives (for example, we conjecture that there
212 is no syntactically correct statement, not a declaration, starting with
213 `SomeIdentifier<...> Function(...` where the angle brackets are balanced).*
Lasse Reichstein Nielsen 2017/07/11 11:13:52 It's not clear to me where we allow parsing Functi
eernst 2017/07/11 15:50:05 The words `the tokens starting at L may be a \`typ
Lasse Reichstein Nielsen 2017/07/12 12:08:06 That's actually not what I'm saying - this does us
eernst 2017/07/12 13:15:38 As discussed IRL, there is no finite bound on the
Lasse Reichstein Nielsen 2017/07/13 06:57:17 Agree. It's worth recognizing that SomeIdentif
214
215 *Note that this disambiguation rule will prevent parsing some otherwise
216 correct programs. For instance, the declaration of an asynchronous function
217 named `Function` with an omitted return type (meaning `dynamic`) and an
218 argument named `int` of type `dynamic` using `Function(int) async {}` will
219 be a parse error, because the parser will commit to parsing a type after
220 having seen "`Function(`" as a lookahead. However, we do not expect that it
221 will be a serious problem for developers to be unable to write such
222 programs.*
223
224 ## Scoping
225
226 Consider a typedef declaration as introduced by this feature, i.e., a
227 construct on the form
228
229 ```
230 metadata 'typedef' identifier typeParameters? '=' functionType ';'
231 ```
232
233 This declaration introduces `identifier` into the enclosing library scope.
234
235 Consider a parameterized typedef, i.e., a construct on the form
236
237 ```
238 metadata 'typedef' identifier typeParameters '=' functionType ';'
239 ```
240
241 Note that in this case the `typeParameters` are present, not optional. This
Lasse Reichstein Nielsen 2017/07/11 11:13:52 Drop the "not optional". Here you are giving a con
eernst 2017/07/11 15:50:04 Dropped `optional`, that's clearly better. By the
242 construct introduces a scope known as the *typedef scope*. Each typedef
243 scope is nested inside the library scope of the enclosing library. Every
244 formal type parameter declared by the `typeParameters` in this construct
245 introduces a type variable into its enclosing typedef scope. The typedef
246 scope is the current scope for the `typeParameters` themselves, and for the
247 `functionType`.
Lasse Reichstein Nielsen 2017/07/11 11:13:52 I'm not sure it's worth it to avoid adding a typed
eernst 2017/07/11 15:50:04 Right. It'll be a small change. I'm not doing it n
248
249 Consider a `functionType` specifying a generic function type, i.e., a
250 construct on the form
251
252 ```
253 returnType? 'Function' typeParameters parameterTypeList
254 ```
255
256 Note again that `typeParameters` are present, not optional. This construct
257 introduces a scope known as a *function type scope*. The function type
258 scope is nested inside the current scope for the associated `functionType`.
259 Every formal type parameter declared by the `typeParameters` introduces a
260 type variable into its enclosing function type scope. The function type
261 scope is the current scope for the entire `functionType`.
262
263 *This implies that parameterized typedefs and function types are capable of
264 specifying F-bounded type parameters, because the type parameters are in
265 scope in the type parameter list itself.*
266
267 ## Static Analysis
268
269 Consider a typedef declaration as introduced by this feature, i.e., a
270 construct on the form
271
272 ```
273 metadata 'typedef' identifier typeParameters? '=' functionType ';'
274 ```
275
276 It is a compile-time error if a name *N* introduced into a library scope by
277 a typedef has an associated `functionType` which depends directly or
Lasse Reichstein Nielsen 2017/07/11 11:13:52 Define "depends". I guess it's actually pretty har
eernst 2017/07/11 15:50:04 This is concerned with static analysis, and depend
278 indirectly on *N*. It is a compile-time error if a bound on a formal type
279 parameter in `typeParameters` is not a type. It is a compile-time error if
280 a typedef has an associated `functionType` which is not a type when
281 analyzed under the assumption that every identifier resolving to a formal
282 type parameter in `typeParameters` is a type. It is a compile-time error if
283 an instantiation *F<T1..Tk>* of a parameterized typedef is mal-bounded.
Lasse Reichstein Nielsen 2017/07/11 11:13:53 Is it? We allow super-bounded generic types in so
eernst 2017/07/11 15:50:05 Aha?! I supported super-bounded types and still do
Lasse Reichstein Nielsen 2017/07/12 12:08:06 Agree that it is a problem, and as discussed it do
eernst 2017/07/12 13:15:37 I'd suggest that we use the current behavior of `d
284
285 *This implies that a typedef cannot be recursive. It can only introduce a
286 name as an alias for a type which is already expressible as a
287 `functionType`, or a name for a type-level function F where every
288 well-bounded invocation `F<T1..Tk>` denotes a type which could be expressed
289 as a `functionType`. Following
290 [common terminology](https://en.wikipedia.org/wiki/Kind_(type_theory)), we
291 could say that a typedef can define entities of kind ` * ` and of kind
292 ` * -> * `, and, when it is assumed that every formal type parameter of the
293 typedef (if any) has kind ` * `, it is an error if the right hand side of the
294 declaration denotes an entity of any other kind than ` * `; in particular,
295 declarations of entities of kind ` * -> * ` cannot be curried.*
Lasse Reichstein Nielsen 2017/07/11 11:13:53 Is this important? Does it affect implementation,
eernst 2017/07/11 15:50:05 It is intended to be helpful for people who know "
296
297 It is a compile-time error if a name declared in a typedef, with or without
298 actual type arguments, is used as a superclass, superinterface, or mixin. It
299 is a compile-time error if a generic function type is used as a bound for a
300 formal type parameter of a class or a function. It is a compile-time error if
301 a generic function type is used as an actual type argument.
302
303 *Generic function types can thus only be used in the following situations:*
304
305 - *as a type annotation on an local, instance, static, or global variable.*
306 - *as a function return or parameter type.*
307 - *in a type test.*
308 - *in a type cast.*
309 - *in an on-catch clause.*
310 - *as a parameter or return type in a function type.*
311
312 *The motivation for having this constraint is that it ensures that the Dart type
313 system admits only predicative types. It does admit non-prenex types, e.g.,
314 `int Function(T function<T>(T) f)`. From research into functional calculi
315 it is well-known that impredicative types give rise to undecidable subtyping,
316 e.g.,
317 [(Pierce, 1993)](http://www2.tcs.ifi.lmu.de/lehre/SS07/Typen/pierce93bounded.pdf ),
318 and even though the Dart type system is very different from F-sub, we cannot
319 assume that these difficulties are absent.*
320
321 ## Dynamic Semantics
322
323 The addition of this feature does not change the dynamic semantics of
324 Dart.
325
326 ## Changes
327
328 2017-May-31: Added constraint on usage of generic function types: They
329 cannot be used as type parameter bounds nor as type arguments.
330
331 2017-Jan-04: Adjusted the grammar to require named parameter types to have
332 a type (previously, the type was optional).
333
334 2016-Dec-21: Changed the grammar to prevent the new function type syntax
335 in several locations (for instance, as a super class or as a mixin). The
336 main change in the grammar is the introduction of `typeWithoutFunction`.
337
338 2016-Dec-15: Changed the grammar to prevent the old style function types
339 (derived from `functionSignature` in the grammar) from occurring inside
340 the new style (`functionType`).
OLDNEW
« no previous file with comments | « no previous file | docs/language/informal/generic-method-syntax.md » ('j') | docs/language/informal/generic-method-syntax.md » ('J')

Powered by Google App Engine
This is Rietveld 408576698