|
| 1 | +# Design Document for Generalized Type Aliases 2018. |
| 2 | + |
| 3 | +Author: [email protected] ( @eernst). |
| 4 | + |
| 5 | +Version: 0.1. |
| 6 | + |
| 7 | + |
| 8 | +## Motivation and Scope |
| 9 | + |
| 10 | +Parameterized types in Dart can be verbose. An example is here: |
| 11 | + |
| 12 | +```dart |
| 13 | +Map<ScaffoldFeatureController<SnackBar, SnackBarClosedReason>, |
| 14 | + SnackBar> |
| 15 | +``` |
| 16 | + |
| 17 | +Such verbose types may be needed repeatedly, and there may also be a need |
| 18 | +for several different variants of them, only differing in |
| 19 | +specific subterms. For instance, we might also need this one: |
| 20 | + |
| 21 | +```dart |
| 22 | +Map<ScaffoldFeatureController<SandwichBar, SandwichBarClosedReason>, |
| 23 | + SandwichBar> |
| 24 | +``` |
| 25 | + |
| 26 | +This type is unlikely to work as intended if the second type argument of |
| 27 | +`Map` is changed to `SnackBar`, but such mistakes could easily happen |
| 28 | +during development. It may not even be easy to detect exactly where the |
| 29 | +mistake occurred, in cases where the erroneous type is used in some |
| 30 | +declaration, and expressions whose types depend on that declaration |
| 31 | +unexpectedly get flagged as compile-time errors, or the IDE completions |
| 32 | +in such expressions fail to list some of the expected choices. |
| 33 | + |
| 34 | +This document describes how type aliases are generalized in Dart 2.2 |
| 35 | +to make such verbose types more concise and consistent. |
| 36 | + |
| 37 | + |
| 38 | +## Feature Specification |
| 39 | + |
| 40 | +The precise definition of the changes is given in the language |
| 41 | +specification, with a proposed form in |
| 42 | +[this CL](https://dart-review.googlesource.com/c/sdk/+/81414). |
| 43 | +The following sections summarize the changes. |
| 44 | + |
| 45 | + |
| 46 | +### Syntax |
| 47 | + |
| 48 | +The grammar is modified as follows in order to support this feature: |
| 49 | + |
| 50 | +``` |
| 51 | +typeAlias ::= |
| 52 | + metadata 'typedef' typeAliasBody | |
| 53 | + metadata 'typedef' identifier typeParameters? '=' type ';' // CHANGED |
| 54 | +``` |
| 55 | + |
| 56 | +*The modification is that a type alias declaration of the form that uses |
| 57 | +`=` can define the type alias to denote any type, not just a function |
| 58 | +type.* |
| 59 | + |
| 60 | + |
| 61 | +### Static Analysis |
| 62 | + |
| 63 | +There is no change in the treatment of existing syntax, so we describe only |
| 64 | +the treatment of syntactic forms that can be new when this feature is added. |
| 65 | + |
| 66 | +The effect of a non-generic type alias declaration of the form that uses |
| 67 | +`=` with name `F` is to bind the name `F` in the library scope of the |
| 68 | +enclosing library to the type denoted by the right hand side of `=` in that |
| 69 | +declaration. |
| 70 | + |
| 71 | +*This is not new, but it applies to a larger set of situations now that the |
| 72 | +right hand side can be any type. Let us call that type on the right hand |
| 73 | +side `T`. This means that `F` can be used as a type annotation, and the |
| 74 | +entity which is given type `F` is considered to have type `T`, with the |
| 75 | +same meaning as in the declaration of `F` (even if some declaration in the |
| 76 | +current library gives a new meaning to an identifier in `T`).* |
| 77 | + |
| 78 | +The effect of a generic type alias declaration of the form |
| 79 | + |
| 80 | +```dart |
| 81 | +typedef F<X1 extends B1 .. Xk extends Bk> = T; |
| 82 | +``` |
| 83 | + |
| 84 | +where metadata is omitted but may be present, is to bind the name `F` in |
| 85 | +the library scope of the enclosing library to a mapping from type argument |
| 86 | +lists to types, such that a parameterized type of the form `F<T1..Tk>` is |
| 87 | +an application of that mapping that denotes the type `[T1/X1 .. Tk/Xk]T`. |
| 88 | + |
| 89 | +Let _F_ be a generic type alias of the form that uses `=` with type |
| 90 | +parameter declarations |
| 91 | +_X<sub>1</sub> extends B<sub>1</sub> .. X<sub>k</sub> extends B<sub>k</sub>_. |
| 92 | +It is a compile-time error unless satisfaction of the declared bounds |
| 93 | +_B<sub>1</sub> .. B<sub>k</sub>_ implies that the right hand side is |
| 94 | +regular-bounded, and all types that occur as subterms of the right hand |
| 95 | +side are well-bounded. |
| 96 | +Any self reference in a type alias, either directly or recursively via another |
| 97 | +type alias, is a compile-time error. |
| 98 | + |
| 99 | +Let `F<T1..Tn>` be a parameterized type where `F` denotes a declaration of |
| 100 | +the form |
| 101 | +```dart |
| 102 | +typedef F<X1 extends B1 .. Xk extends Bk> = T; |
| 103 | +``` |
| 104 | +where metadata is omitted but may be present. It is a compile-time error if |
| 105 | +`n != k` and it is a compile-time error if `F<T1..Tn>` is not well-bounded. |
| 106 | + |
| 107 | +*These errors are not new, but they apply to a larger set of situations now |
| 108 | +that the right hand side can be any type.* |
| 109 | + |
| 110 | +When a `typeName` (*that is, `identifier` or `identifier.identifier`*) |
| 111 | +that resolves to a generic type alias declaration is used as a type or |
| 112 | +evaluated as an expression, it is subject to instantiation to bound. |
| 113 | + |
| 114 | +*This treatment of generic type aliases is again the same as it was |
| 115 | +previously, but it involves a larger set of types.* |
| 116 | + |
| 117 | +*Note that type aliases introduce types and not classes. Consequently, a |
| 118 | +type alias can **not** be used in a position where a class is expected: in the |
| 119 | +`extends`, `with`, `implements`, or `on` clause of a class or mixin |
| 120 | +declaration; for a static member access; or in an instance creation |
| 121 | +expression (`new F()`, `const F<int>()`). On the other hand, it **can** be |
| 122 | +used as a type annotation, as a type argument, as part of a function type |
| 123 | +or function signature, as a type literal, in an `on` clause of a `try` |
| 124 | +statement, in a type test (`e is F`), and in a type cast (`e as F`).* |
| 125 | + |
| 126 | + |
| 127 | +### Dynamic Semantics |
| 128 | + |
| 129 | +*The dynamic semantics relies on elaborations on the program performed |
| 130 | +during compilation. In particular, instantiation to bound has occurred, and |
| 131 | +hence some `typeName`s in the source code have been transformed into |
| 132 | +parameterized types, even when a type is used as an expression such that it |
| 133 | +would be a syntax error to actually add the type arguments explicitly in |
| 134 | +the source program. This means that every generic type alias receives |
| 135 | +actual type arguments at all locations where it is used. At run time it is |
| 136 | +also known that the program has no compile-time errors.* |
| 137 | + |
| 138 | +For dynamic type checks, type tests, and expression evaluations, an |
| 139 | +identifier `F` resolving to a non-generic type alias and a parameterized |
| 140 | +type `F<T1..Tk>` where `F` resolves to a generic type alias are treated |
| 141 | +identically to the same type checks and type tests performed with the type |
| 142 | +denoted by that identifier or parameterized type, and the evaluation of `F` |
| 143 | +respectively `F<T1..Tk>` as an expression works the same as evaluation of a |
| 144 | +fresh type variable bound to the denoted type. |
| 145 | + |
| 146 | + |
| 147 | +## Versions |
| 148 | + |
| 149 | +* Nov 6th, 2018, version 0.1: Initial version of this document. |
0 commit comments