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

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

Powered by Google App Engine
This is Rietveld 408576698