@@ -4,7 +4,7 @@ Author: Bob Nystrom, Lasse Nielsen
44
55Status: Accepted
66
7- Version 1.5
7+ Version 1.6
88
99Experiment flag: class-modifiers
1010
@@ -514,9 +514,7 @@ Many combinations don't make sense:
514514* ` sealed ` types cannot be constructed so it's redundant to combine with
515515 ` abstract ` .
516516* ` sealed ` types cannot be extended or implemented, so it's redundant to
517- combine with ` final ` .
518- * ` sealed ` types cannot be extended so it contradicts ` base ` .
519- * ` sealed ` types cannot be implemented, so it contradicts ` interface ` .
517+ combine with ` final ` , ` base ` , or ` interface ` .
520518* ` sealed ` types cannot be mixed in outside of their library, so it
521519 contradicts ` mixin ` on a class. * It's useful to allow ` sealed ` on a mixin
522520 declaration because the mixin can be applied within the same library.
@@ -933,8 +931,139 @@ as a mixin. If the class defines a generative constructor or extends anything
933931other than ` Object ` , then it already cannot be used as a mixin and no change is
934932needed.
935933
934+ ## Implementation and documentation suggestions for usability
935+
936+ * This section is non-normative. It's a set of suggestions to implementation and
937+ documentation teams to help ensure that the feature is easy for users to use and
938+ discover.*
939+
940+ ### Errors, error recovery, and fixups
941+
942+ First of all, to the extent that it's reasonably feasible to do so, we should
943+ try to make the parser understand that any time it sees a top level sequence of
944+ any of the keywords ` sealed ` , ` abstract ` , ` final ` , ` interface ` , ` base ` , ` mixin ` ,
945+ or ` class ` , the user is trying to declare something class-like or mixin-like,
946+ even if they left out an important keyword, used conflicting keywords, or put
947+ keywords in the wrong order. That way we can issue errors whose IDE fixups will
948+ help the user clean up their class or mixin declaration, rather than just
949+ ` unexpected { ` or something. For example, this should be recognized by the
950+ parser as an attempt to make a mixin or class:
951+
952+ ``` dart
953+ interface sealed C {
954+ ...
955+ }
956+ ```
957+
958+ (The parser will obviously issue an error, but it should still fire the
959+ appropriate events to allow the analyzer to create a ` ClassDeclaration ` AST
960+ node, and it should analyze the things inside the curly braces as class
961+ members).
962+
963+ If the keywords aren't in the proper order (` sealed ` /` abstract ` , then
964+ ` final ` /` interface ` /` base ` , then ` mixin ` , then ` class ` ), or if a keyword was
965+ repeated, the parser error should be on the first keyword token that's out of
966+ order or repeated, and the fixup should offer to fix the order by sorting and
967+ de-duplicating the keywords appropriately. So in the example above, the "wrong
968+ order" error should be on the keyword ` sealed ` , and the fixup should change it
969+ to ` sealed interface ` , which is still an error for other reasons, but is at
970+ least in the right order now.
971+
972+ With order and duplication out of the way, that leaves 127 possible combinations
973+ of the 7 keywords. The remaining error cases (and their associated IDE fixups)
974+ are:
975+
976+ - Did you say both ` abstract ` and ` sealed ` ? Drop ` abstract ` ; it’s redundant.
977+ Now there's only 95 possibilities.
978+
979+ - Did you say both ` interface ` and ` final ` ? Drop ` interface ` ; it’s redundant.
980+ Now there's only 71 possibilities.
981+
982+ - Did you say both ` base ` and ` final ` ? Drop ` base ` ; it’s redundant. Now
983+ there's only 59 possibilities.
984+
985+ - Did you say both ` interface ` and ` base ` ? Say ` final ` instead. Now there's
986+ only 47 possibilities.
987+
988+ - Did you say neither ` mixin ` nor ` class ` ? You have to pick one or the other or
989+ both. The fixup can probably safely assume you mean ` class ` . (Exception: if
990+ you just said ` interface ` and no other keywords, you probably mean `abstract
991+ class`). Now there's only 36 possibilities.
992+
993+ - Did you say both ` sealed ` and ` final ` ? Drop ` final ` ; it’s redundant. Now
994+ there's only 33 possibilities.
995+
996+ - Did you say both ` sealed ` and ` base ` ? Drop ` base ` ; it’s redundant. Now
997+ there's only 30 possibilities.
998+
999+ - Did you say both ` sealed ` and ` interface ` ? Drop ` interface ` ; it’s redundant.
1000+ Now there's only 27 possibilities.
1001+
1002+ - Did you say both ` mixin ` and ` class ` , as well as one of the following
1003+ keywords: ` sealed ` , ` interface ` , or ` final ` ? Drop ` class ` and replace
1004+ ` extends M ` with ` with M ` wherever it appears in your library. Now there's
1005+ only 22 possibilities.
1006+
1007+ - Did you say both ` abstract ` and ` mixin ` , but not ` class ` ? Drop ` abstract ` ;
1008+ it’s redundant. Now we are down to the 18 permitted possibilities.
1009+
1010+ If we take this sort of approach, then users who don't love reading
1011+ documentation will be able to just experimentally string together combinations
1012+ of the keywords we've made available to them, and the errors and fixups will
1013+ guide them to something valid, and then they can play around and see the effect.
1014+
1015+ ### Introducing the feature to users
1016+
1017+ If we assume that most users will have access to the IDE fixups noted above, it
1018+ suggests that a nice way to introduce the feature to folks would be to gloss
1019+ over what combinations are redundant or contradictory, and just tell them in
1020+ plain English what each keyword does. Users who love reading documentation can
1021+ read further and find out which combinations are prohibited; users who don't can
1022+ just try them out, and the IDE will train them which combinations are valid over
1023+ time. So the core of the feature becomes explainable in just seven lines, three
1024+ of which are just restatements of things the user was already familiar with.
1025+ Something like:
1026+
1027+ - ` sealed ` means "this type has a known set of direct subtypes, so switching on
1028+ it will require the switch to be exhaustive".
1029+
1030+ - ` abstract ` means "this type can't be constructed directly", but you already
1031+ knew that. It's only included in the list to help clarify that ` abstract ` is
1032+ one of the seven keywords users should try combining together at the top of
1033+ your declaration.
1034+
1035+ - ` interface ` means "this type can't be extended from outside this library".
1036+
1037+ - ` base ` means "this type can't be implemented from outside this library".
1038+
1039+ - ` final ` means "this type can neither be extended nor implemented from outside
1040+ this library".
1041+
1042+ - ` mixin ` means "this type can be used in mixin-like ways, i.e. it can appear in
1043+ the 'with' clause of other classes". Granted, this is kind of a circular
1044+ definition, but this explanation is intended for programmers familiar with
1045+ Dart 2.19, and they're already familiar with mixins.
1046+
1047+ - ` class ` means "this type can be used in class-like ways, i.e. it can be
1048+ extended, or constructed, unless otherwise forbidden". Again, this is a
1049+ circular definition, but our audience obviously already knows what classes
1050+ are. Including it in the list helps make it clear that we're putting mixins
1051+ and classes on equal footing, and helps clarify why ` mixin class ` is a
1052+ reasonable thing.
1053+
1054+ (Note that this list is deliberately in the order required by the grammar).
1055+
1056+ Obviously there are plenty of details left out of this description. But
1057+ hopefully it should be enough to get people started using the feature, and the
1058+ errors and fixups would help keep them on the rails.
1059+
9361060## Changelog
9371061
1062+ 1.6
1063+
1064+ - Add implementation suggestions about errors, error recovery, and fixups for
1065+ class modifiers.
1066+
93810671.5
9391068
9401069- Fix mixin application grammar to match prose where ` mixin ` can't be applied
0 commit comments