Sealed types

forax at univ-mlv.fr forax at univ-mlv.fr
Sat Dec 1 12:46:46 UTC 2018


> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Vendredi 30 Novembre 2018 16:30:45
> Objet: Re: Sealed types

>>> sealed interface Node { ... }

>>> In this streamlined form, Node may be extended only by its nestmates. This may
>>> be suitable for many situations, but not for all; in this case, the user may
>>> specify an explicit permits list:
>>> sealed interface Node
>>>     permits FooNode, BarNode { ... }

>>> The two forms may not be combined; if there is a permits list, it must list all
>>> the permitted subtypes. We can think of the simple form as merely inferring the
>>> permits clause from information in the same compilation unit.

>> what inferring from the same compilation unit means ?

> It means: if there is no "permits" list, the compiler is allowed to synthesize
> one that contains exactly the list of subtypes declared in the same compilation
> unit. (Much like the way we synthesize the NestMembers attribute.)

>> - it works for anonymous class declared in the same compilation unit ?

> Anonymous classes are a good question. On the one hand, I can imagine when they
> might be useful. On the other, it means there's no way anyone -- including the
> current class, to which all the subtypes are accessible -- will be able to
> switch exhaustively over them. I think we should consider banning them, as you
> can always fall back to a named class. The exhaustiveness part is important,
> and too easy to forget about when you're writing the class.
Exhaustiveness is one consequence of a sealed interface, but having a sealed interface, i.e. constraining all subtypes to be defined in the same compilation unit is useful even without exhaustiveness. 
It's a way to allow to make an interface visible without having to care about implementations you do not know. 

In a previous mail, you said that if an implementation class is not visible from a switch, then the compiler will just ask for a default. 
An anonymous class (or a local class of a method for completeness) is just a class which is not visible from any switches. 

> (Other restrictions: classes in the permits list must be in the same module (or
> if not in a module, same package and protection domain) as the sealed type, and
> must be accessible to the sealed type.)
For me, a sealed type and all its implementation are nestmates from the VM point of view (it's stronger that you are proposing), they are part of the same compilation unit. 

If you have a hierarchy of sealed interfaces in the same compilation unit 
sealed interface Foo { } 
record FooImpl implements Foo { } 
sealed interface Bar extends Foo { } 
record BarImpl implements Bar {} 

the compiler generates that 
- Foo permits FooImpl and Bar 
- Bar permits BarImpl 
- Foo, FooImpl, Bar and BarImpl are nestmates 

>> - it works for functional interface for lambda in the same compilation unit ?

> Same as for anon classes; if we ban one, we ban the other.

>> what exactly the permit list contains ??
>> (dynamic nestmate/sealed class to the rescue ?)

> I don't seen an interaction with dynamic nestmates. The permits list is an
> explicit list; if the dynamic class is not on the list, it can't be a subtype,
> even if its in the nest. If we infer the list, we're not recording "all
> nestmates", but instead inferring it to be the list of nestmates known at
> compile time.
see above, if we allow anonymous class, we have to allow lambdas (we already support refactoring from one to the other). 
Given that the class of a lambda is only knows at runtime, it means that the nestmate attributes and the sealed attributes have to be updated at runtime. 

>> And in another compilation unit
>> Fun fun = x -> 2 * x; // rejected because Fun is sealed ?

> Yep.

>>> Note: It might be allowable for VM support to follow in a later version, rather
>>> than delaying the feature entirely.

>> Does seems to be a good idea to divorce the two. It means you can create classes
>> with ASM that will work for a version of the VM but not the next one.

> I think you mean "does not seem to be a good idea"? I agree, it's got problems,
> such as the ones you raise. But it is a possible move we can make, so we can
> keep it on the board until we have more visibility into the cost of waiting for
> VM support.
Ok ! 

>>> Accessibility. Subtypes need not be as accessible as the sealed parent. In this
>>> case, 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.”)

>> Yes !
>> I expect a public sealed interface with several package private implementations
>> to be a common pattern.

> I agree qualitatively (and share your conclusion), but disagree with the
> quantitative statement here ("common"). I think it will be common _for platform
> and low-level library implementors_ to do this. But if the feature is
> successful, this will probably be a tiny fraction of the sealed types in the
> world. So yes, this is an important pattern, but I hope it is UNcommon.
yes, it will be common for library implementors 

>> The implication is that you can not define (at least now) the implementations
>> inside the sealed interface because all class members of an interface can only
>> be public so you can not declare a package-private implementation. I don't know
>> exactly why private class are not allowed in interface, it seems to be an
>> overlook for me.

> Yes, this (and fields too, even though they kind of suck in interfaces), was
> largely overlooked. We are gathering a list of "gratuitous nesting
> constraints", of which this is one, and at some point will bang them all out.
> (Restrictions on static members in nested classes is another. Local interfaces
> and enums is another. Maybe even local methods. Etc.)
yes, not having local interface is weird too. 
I'm in for local methods iff we come with a syntax that is not too close to a local variable declaration; 

Rémi 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20181201/7c6db010/attachment.html>


More information about the amber-spec-experts mailing list