How about “permits class”? (if you don’t like that, then all I have left to offer are “permits catch” and “permits goto”. :-)
> On Dec 7, 2018, at 12:50 PM, Brian Goetz <brian.go...@oracle.com> wrote: > > The most obvious remaining question appears to be: how do we spell "permits > <nest-members>". > > We could say "permits this", which has the advantage of being a keyword, but > doesn't really mean what it says. > > We could say "permits local", though "local" usually means local to a method. > > > We could say "permits nest" or "permits nested", though if we decide to allow > public auxilliary subclasses in this case, then they are not likely to be > nestmates, and surely not nested. > > We could allow the permits clause to be omitted, and be interpreted as > "permits nest", but this would much more severely change the meaning of final > -- people have an idea what final means, and this surely wouldn't be it. > > Open to other ideas... > > (This is one of the costs of retconning final; if we used "sealed", we > wouldn't need a permits clause at all. Overall its probably still a good > tradeoff, but we do have to paint this shed acceptably.) > > > > On 12/7/2018 11:38 AM, Brian Goetz wrote: >> I’ve updated the document on sealing to reflect the discussion so far. >> Sealed Classes >> >> Definition. A sealed type is one for which subclassing is restricted >> according to guidance specified with the type’s declaration; finality can be >> considered a degenerate form of sealing, where no subclasses at all are >> permitted. Sealed types are a sensible means of modeling algebraic sum types >> in a nominal type hierarchy; they go nicely with records (algebraic product >> types), though are also useful on their own. >> >> Sealing serves two distinct purposes. The first, and more obvious, is that >> it restricts who can be a subtype. This is largely a declaration-site >> concern, where an API owner wants to defend the integrity of their API. The >> other is that it potentially enables exhaustiveness analysis at the use site >> when switching over sealed types (and possibly other features.) This is less >> obvious, and the benefit is contingent on some other things, but is valuable >> as it enables better compile-time type checking. >> >> Declaration. We specify that a class is sealed by applying the final >> modifier to a class, abstract class, interface, or record, and specifying a >> permits list: >> >> final interface Node >> permits A, B, C { ... } >> In this explicit form, Node may be extended only by the types enumerated in >> the permits list (which must further be members of the same package or >> module.) >> >> In many situations, this may be overly explicit; if all the subtypes are >> declared in the same compilation unit, we may wish to permit a streamlined >> form of the permits clause, that means “may be extended by classes in the >> same compilation unit.” >> >> final interface Node >> permits __nestmates { ... } >> (As usual, pseudo-keywords beginning with __ are placeholders to illustrate >> the overall shape of the syntax.) >> >> We can think of the simpler form as merely inferring the full permits clause >> from information already present in the source file. >> >> Anonymous subclasses (and lambdas) of a sealed type are prohibited. >> >> Exhaustiveness. One of the benefits of sealing is that the compiler can >> enumerate the permitted subtypes of a sealed type; this in turn lets us >> perform exhaustiveness analysis when switching over patterns involving >> sealed types. (In the simplified form, the compiler computes the permits >> list by enumerating the subtypes in the nest when the nest is declared, >> since they are in a single compilation unit.) >> >> Note: It is superficially tempting to say permits package or permits module >> as a shorthand, which would allow for a type to be extended by package-mates >> or module-mates without listing them all. However, this would undermine the >> compiler’s ability to reason about exhaustiveness, because packages and >> modules are not always co-compiled. This would achieve the desired >> subclassing restrictions, but not the desired ability to reason about >> exhaustiveness. >> >> Classfile. In the classfile, a sealed type is identified with an ACC_FINAL >> accessibility bit, and a PermittedSubtypes attribute which contains a list >> of permitted subtypes (similar in structure to the nestmate attributes.) >> Classes with ACC_FINAL but without PermittedSubtypes behave like traditional >> final classes. >> >> Sealing is inherited. Unless otherwise specified, abstract subtypes of >> sealed types are implicitly sealed, and concrete subtypes are implicitly >> final. This can be reversed by explicitly modifying the subtype with >> non-final. >> >> Unsealing a subtype in a hierarchy doesn’t undermine all the benefits of >> sealing, because the (possibly inferred) set of explicitly permitted >> subtypes still constitutes a total covering. However, users who know about >> unsealed subtypes can use this information to their benefit (much like we do >> with exceptions today; you can catch FileNotFoundException separately from >> IOException if you want, but don’t have to.) >> Note: Scala made the opposite choice with respect to inheritance, requiring >> sealing to be opted into at all levels. This is widely believed to be a >> source of bugs; it is relatively rare that one actually wants a subtype of a >> sealed type to not be sealed, and in those cases, is best to be explicit. >> Not inheriting would be a simpler rule, but I’d rather not add to the list >> of “things for which Java got the defaults wrong.” >> >> An example of where explicit unsealing (and private subtypes) is useful can >> be found in the JEP-334 API: >> >> final interface ConstantDesc >> permits String, Integer, Float, Long, Double, >> ClassDesc, MethodTypeDesc, MethodHandleDesc, >> DynamicConstantDesc { } >> >> final interface ClassDesc extends ConstantDesc >> permits PrimitiveClassDescImpl, ReferenceClassDescImpl { } >> >> private class PrimitiveClassDescImpl implements ClassDesc { } >> private class ReferenceClassDescImpl implements ClassDesc { } >> final interface MethodTypeDesc extends ConstantDesc >> permits MethodTypeDescImpl { } >> >> final interface MethodHandleDesc extends ConstantDesc >> permits DirectMethodHandleDesc, MethodHandleDescImpl { } >> final interface DirectMethodHandleDesc extends MethodHandleDesc >> permits DirectMethodHandleDescImpl { } >> >> // designed for subclassing >> non-final class DynamicConstantDesc extends ConstantDesc { ... } >> Enforcement. Both the compiler and JVM should enforce sealing, as they both >> enforce finality today (though from a project-management standpoint, it >> might be allowable for VM support to follow in a later version, rather than >> delaying the feature entirely.) >> >> Accessibility. Subtypes need not be as accessible as the sealed parent. In >> this case, some clients are not going to get the chance to exhaustively >> switch over them; they’ll have to make these switches exhaustive with a >> default clause or other total pattern. When compiling a switch over such a >> sealed type, the compiler can provide a useful error message (“I know this >> is a sealed type, but I can’t provide full exhaustiveness checking here >> because you can’t see all the subtypes, so you still need a default.”) >> >> Javadoc. The list of permitted subtypes should be incorporated into the >> Javadoc. Note that this is not exactly the same as the current “All >> implementing classes” list that Javadoc currently includes, so a >> list like “All permitted subtypes” might be added (possibly with some >> indication if the subtype is less accessible than the parent, or including >> an annotation that there exist others that are not listed.) >> >> Open question: With the advent of records, which allow us to define classes >> in a single line, the “one class per file” rule starts to seem both a little >> silly, and constrain the user’s ability to put related definitions together >> (which may be more readable) while exporting a flat namespace in the public >> API. I think it is worth considering relaxing this rule to permit for sealed >> classes, say: allowing public auxilliary subtypes of the primary type, if >> the primary type is public and sealed. >> >