From maurizio.cimadamore at oracle.com Tue May 23 17:49:55 2017 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 23 May 2017 18:49:55 +0100 Subject: [enhanced enums] - end of the road? Message-ID: <786de6e1-8ca4-48ab-24b4-57460c232cef@oracle.com> 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, 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, as follows: D("-d", ...) While an option for which multiple choices are available, could be encoded using an enum as a type-argument - for instance: PROC("-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>("-g:", ...) where DebugOption would be defined as follows: enum DebugOption { LINES, VARS, SOURCE; } So, instead of storing all options into a Map, we could store them into a Map. Then, we would turn the Options.get method from this: public String get(String option) { ... } to something like this: public Z get(Option 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