[enhanced enums] - end of the road?

Peter Levart peter.levart at gmail.com
Thu Jun 15 11:43:26 UTC 2017


Hi Maurizio,

What if the enum type was kept non-generic, but there could optionally 
be a designated generic supertype inserted between the enum type and the 
constant type. For example:

public enum Option implements Consumer<String> super Generic {
     D<String>("-d", ...),
     PROC<ProcOption>("-proc", ...),
     ...;

     class Generic<T> implements Function<String, T> {
         Generic(...) {
             super(...);
         }

         public T apply(String s) {
             ...
         }
     }

     Option(...) {
         ...
     }

     public void accept(String s) {
         ...
     }
}


this would translate to:

public class Option extends Enum<Option> implements Consumer<String> {

     public static final Generic<String> D = new Generic<>("D", 0, "-d", 
...);
     public static final Generic<ProcOption> PROC = new 
Generic<>("PROC", 1, "-proc", ...);
     ...

     static class Generic<T> extends Option implements Function<String, T> {
         Generic(String name, int ordinal, ...) {
              super(name, ordinal, ...);
         }

         public T apply(String s) {
             ...
         }
     }

     Option(String name, int ordinal, ...) {
         super(name, ordinal);
         ...
     }

     public void accept(String s) {
         ...
     }
}


The "super" keyword in enum declaration could only designate a class in 
the same compilation unit - the enum member static class.

Hm...


Regards, Peter

On 05/23/2017 07:49 PM, Maurizio Cimadamore wrote:
> Hi,
> over the last few weeks we've been carrying out experiments to test 
> the feasibility of the enhanced enums feature. As described in the 
> JEP, this feature is particularly powerful as it allows enums 
> constants to be carrier of generic type information, which can then be 
> fed back to the inference machinery.
>
> One experiment we wanted to make was to see if enhanced enums could 
> make javac's own Option [1] enum better. This enum defines a bunch of 
> constants, one for each supported javac command line arguments (e.g 
> -classpath, -d, etc.). Furthermore, the enum defines method so that 
> each constant can be parsed given the javac command line and 
> processed, accordingly to some OptionHelper. Most options, along with 
> the value of their arguments would simply be stored into a Map<String, 
> String>, which is the backbone of the Options class [2].
>
> One problem with storing option values as Strings is that clients need 
> to do the parsing. So, if an option has an integer argument, it's up 
> to the client to get the value of that option off the map, parse it 
> into a number and (maybe) check as to whether the range makes sense.
>
> With enhanced enums it should be possible to do better than this; more 
> specifically, if enums supported generics, each option could specify a 
> type argument - that is, the type of the argument that javac expects 
> for that particular option.
>
> So, an option with a plain String argument would be encoded as 
> Option<String>, as follows:
>
> D<String>("-d", ...)
>
> While an option for which multiple choices are available, could be 
> encoded using an enum as a type-argument - for instance:
>
> PROC<ProcOption>("-proc", ...)
>
> where ProcOption would be defined as follows:
>
> enum ProcOption {
>    NONE, ONLY;
> }
>
> Finally, for an option whose argument can be a set of values, we would 
> use the following encoding:
>
> G_CUSTOM<EnumSet<DebugOption>>("-g:",  ...)
>
> where DebugOption would be defined as follows:
>
> enum DebugOption {
>     LINES, VARS, SOURCE;
> }
>
> So, instead of storing all options into a Map<String, String>, we 
> could store them into a Map<Option, Object>. Then, we would turn the 
> Options.get method from this:
>
> public String get(String option) { ... }
>
> to something like this:
>
> public Z get(Option<Z> option) { ... }
>
> granted, there will be some unchecked operations carried out by the 
> body of this method, but the map should be well-constructed by design, 
> so it should be safe. What we get back is that now clients can do 
> things like:
>
> boolean g_vars = options.get(Option.G_CUSTOM).contains(DebugOption.VARS);
>
> Note how we raised the expressiveness level of the client, which no 
> longer has to do parsing duties (and domain conversions). So, that was 
> the experiment we wanted to carry out - ultimately, this is the kind 
> of stuff you'd like to be writing with enhanced enums, so this seemed 
> like a reasonably comprehensive test for the feature.
>
>
> Unfortunately, the results of the experiment were not as successful as 
> we'd hoped. As soon as we turned the Option enum into a generic class 
> (by merely adding a type parameter in its declaration), we immediately 
> started hitting dozens of compile-time errors. The errors were rather 
> cryptic, all pointing to some obscure failure when calling 
> EnumSet.noneOf or EnumSet.allOf with the newly generified Option 
> class. In other words, code like this:
>
> EnumSet.noneOf(Option.class)
>
> Was now failing. The issue that was underpinning all these failures is 
> - in retrospect - rather obvious: the following type:
>
> EnumSet<Option>
>
> is *not* a well-formed type if Option is a generic class. Why? Well, 
> EnumSet is declared like this:
>
> class EnumSet<*E extends Enum<E>*> { ... }
>
> which means the type parameter has an f-bound. In concrete terms, we 
> have to check that:
>
> Option <: [E:=Option]Enum<E>
>
> That is, the actual type-argument must conform to its declared bound. 
> But if we follow that check, we obtain:
>
> Option <: [E:=Option]Enum<E>
> Option <: Enum<Option>
> Enum (*) <: Enum<Option>
> false
>
> (*) note that Option is now a 'raw' type - and a raw type has all 
> supertypes erased, as per JLS 4.8.
>
> In other words, there's no way to write down the type of an enum set 
> which contains heterogeneous options - the wildcard path doesn't help 
> either:
>
> EnumSet<Option<?>>
>
> As, the above check would develop in the following way:
>
> Option<?> <: [E:=Option<?>]Enum<E>
> Option<?> <: Enum<Option<?>>
> Enum<#CAP> (**) <: Enum<Option<?>>
> false
>
> (**) the supertype of a wildcard-parameterized type is obtained by 
> first capturing, and then recursing to the supertype, as per JLS 4.10.2
>
>
> In other words, generic enums are not interoperable with common data 
> structures such as enum sets (and, more generally, with any f-bounded 
> generic data structure).
>
> While we could just deliver the part of JEP 301 regarding sharper 
> typing of enum constants and leave generic enums alone, we feel 
> there's not much value into pursuing that path alone. After all, the 
> benefits of enhanced enums were exactly in combining sharper typing 
> with generic type information, so that enum constants could be used as 
> type carriers. If generic enums are not viable, then much of the 
> usefulness of this JEP is lost.
>
> It is unclear at this point in time if type system improvements (which 
> we are pursuing as part of a separate activity [Dan is there a link 
> for this??]) would ameliorate the situation.
>
> Until we figure this out, I suggest that we put this JEP on hold for 
> the time being
>
> [1] - 
> http://hg.openjdk.java.net/jdk10/jdk10/langtools/file/tip/src/jdk.compiler/share/classes/com/sun/tools/javac/main/Option.java
> [2] - 
> http://hg.openjdk.java.net/jdk10/jdk10/langtools/file/tip/src/jdk.compiler/share/classes/com/sun/tools/javac/util/Options.java#l48 
>
>
>
> Cheers
> Maurizio
>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20170615/f1c9aa3f/attachment-0001.html>


More information about the amber-spec-experts mailing list