[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