Refinements for sealed types

Brian Goetz brian.goetz at oracle.com
Tue Aug 20 22:45:49 UTC 2019


> I see two committed principles here:
> 
> 1. Permitted subtypes are common; their enumeration isn't. It's almost like explicit `permits` is a separable feature that could be set aside for now.
> 
> 2. Ceremony reduction is important enough to treat concrete direct subclasses of a sealed type specially.

I am not sure I would raise these to the level of “principle”, as much as “expectation”.  In the typical co-declared sum type case, there may be many cases:

    sealed interface Letter {
        class A implements Letter { }
        class B implements Letter { }
        …
        class Z implements Letter { }
    }

Having to say “permits A, B, .. Z” here is both verbose (lots of cases) and seems pretty repetitive (the subtypes are RIGHT THERE.)  Same, to a lesser degree, with having to say “sealed” or “final” on each case.  

In the typical “restricted hierarchy” case, I expect there to be fewer subtypes (less fan-out), less co-declaration (will likely want to use top level classes), and simply fewer instances of this use (mostly, this will be used by platforms and libraries, rather than applications.)  So the ceremony here is (a) less substantial, (b) less frequent, and (c) more beneficial.  

So basically, this is special treatment for co-declared sums.  (We’re still more verbose than Haskell, even with these, sadly.)

> However, that special treatment comes at the expense of another principle: "leaf nodes [implicitly final classes] are obvious". I was hoping to have this because implicitly final classes [sealed with zero permitted subtypes] are The Show. Below, it's kinda hard to figure out that class C is NOT a leaf node:
> 
> --I.java--
> sealed interface I {}    // implicitly `permits C`
> 
> class C implements I {}  // implicitly `sealed` and `permits D`
> 
> ... lots of other stuff ...
> class D extends C {}     // implicitly `sealed` w/ no permitted subtypes
> ----------
> 
> whereas below, the leaf nodes are mostly obvious by virtue of how the intermediate hierarchy is explicitly `sealed`:
> 
> --I.java--
> sealed interface I {}            // implicitly `permits J`
> sealed interface J extends I {}  // plainly not a leaf
> sealed abstract class AC implements J {}  // plainly not a leaf
> 
> class PolarData extends AC {}      // maybe a leaf
> class CartesianData extends AC {}  // maybe a leaf
> // EOF, so yes, PolarData and CartesianData are indeed leaves
> ----------
> 
> tl;dr We're back to having both sealed-ness and the potentially-non-trivial permits list be implicit, which was earlier thought to strain the reader. If we said that concrete direct subclasses of a sealed type were implicitly final, then ceremony reduction would still be high, and if you really don't want them to be leaf nodes then say `sealed` or `non-sealed`.

This seems reasonable too, because most concrete classes in such hierarchies _will be_ leaves.  In this world, though, it feels a little inconsistent to only do this when the subclass and the sealed type are in the same compilation unit; we could extend this to all concrete subclasses of sealed types (implicitly final, unless they say sealed or non-sealed).  This is consistent, but feels a little more action-at-a-distance-y.  So, its pick your poison.




More information about the amber-spec-experts mailing list