enhanced enums - back from the dead?

forax at univ-mlv.fr forax at univ-mlv.fr
Fri Dec 7 10:26:24 UTC 2018

----- Mail original -----
> De: "Maurizio Cimadamore" <maurizio.cimadamore at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Jeudi 6 Décembre 2018 20:23:05
> Objet: Re: enhanced enums - back from the dead?

> On 06/12/2018 16:53, forax at univ-mlv.fr wrote:
>> ----- Mail original -----
>>> De: "Maurizio Cimadamore" <maurizio.cimadamore at oracle.com>
>>> À: "Remi Forax" <forax at univ-mlv.fr>
>>> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
>>> Envoyé: Jeudi 6 Décembre 2018 09:54:09
>>> Objet: Re: enhanced enums - back from the dead?
>>> Hi Remi, some comments inline.
>>> On 05/12/2018 21:43, Remi Forax wrote:
>>>> Hi Maurizio,
>>>> i think you have overlook the fact that raw types and inference also doesn't
>>>> play well together.
>>> As I said, I've played with this quite a bit and came out convinced that
>>> usability wise it's good. Note that in the proposed model, enum
>>> constants will have full generic types - e.g. Foo<String>; it's only
>>> when you go to the supertype that the type system will say Enum<Foo>.
>>> But this will only be used by APIs accepting some Enum<T> - so we're
>>> fine, and this actually guarantees same inference results as before
>>> generification of a given enum.
>> yes, i'm worried about Foo.values()
>>    public enum Foo<T extends Comparable<T>> implements Comparable<Foo<T>> {
>>      S<>(""), I<>(42);   // JEP 301 mentions the diamond syntax
>>      private final T t;
>>      private Foo(T t) {
>>        this.t = t;
>>      }
>>      @Override
>>      public int compareTo(Foo<T> o) {
>>        return t.compareTo(o.t);
>>      }
>>      public static void main(String[] args) {
>>        Arrays.stream(Foo.values()).sorted().forEach(System.out::println);
>>      }
>>    }
> Can't quite understand what you mean - the code above is flawed in
> different ways; first you can't implement Comparable<Foo<T>> - as Enum
> already does that and with Comparable<Foo> in this case (because of the
> new treatment).
> Secondly, you can't override compareTo - which is final in enum.
> So, the correct example is this:
> import java.util.*;
> enum Foo<T extends Comparable<T>> implements Comparable<Foo> {
>     S<String>(""), I<Integer>(42);   // JEP 301 mentions the diamond syntax
>     private final T t;
>     private Foo(T t) {
>       this.t = t;
>     }
>     public static void main(String[] args) {
> Arrays.stream(Foo.values()).sorted().forEach(System.out::println);
>     }
>   }
> Which compiles with no issues.

yes, i wanted a recursive bound and forget that an enum already implements Comparable.
so let's retry

public enum Foo<T extends Comparable<T>> {
  S<String>(""), I<Integer>(42);   // JEP 301 mentions the diamond syntax
  private T t;
  public Foo(T t) {
    this.t = t;
  T t() {
    return t;
  public static void main(String[] args) {

My original point was, when you introduce a raw type in a Stream, you end up with a warning on every methods because of the inference.
While if you use the correct type, with an unbounded wildcard, the compiler stop you to do stupid things,
basically, you are trading an error to a series of warning people will be happy to suppress with @SuppressWarnings.

  emits a series of warnings

  Stream.of(S, I).sorted(Comparator.comparing(Foo::t)).forEach(System.out::println);
  emits an error

BTW, it seems there is an issue somewhere in the compiler because
happily compiles ??

>>>> accessibility:
>>>> Widening the type is usually a big No because of the security implication. The
>>>> fact that the same code code has no security bug with version n but a security
>>>> hole with version n + 1 scares me.
>>> What scenario do you have in mind regarding enum constant pseudo-inner classes?
>> any scenario that is using a Lookup object
> Still not getting what is the security implication; one thing is  to say
> that you can reflectively inspect a class where you could not before;
> another is that doing so represents a vulnerability. The proposed rule
> says that the enum constant class gets same visibility as parent. So you
> are really saying that some constant class contains _security sensitive_
> details, and that users relied on an *unspecified* compiler behavior
> that protected them, as javac made the class package-private. That seems
> a pretty strange argument.

You can inspect any class even the private one by reflection, but you can not call the constructor if the class is private, if the class is package private, the constructor is package private too so you are widening the access.

>>>> source compatibility:
>>>> It's may not be a big issue because the JDK source doesn't use 'var'. If a code
>>>> uses 'var' the sharp type will propagate more, so the JDK is not perhaps the
>>>> best code to test.
>>>> friend or foe:
>>>> the rules for raw types are brutal as you said, but it's by design, it offers
>>>> maximum compatibility and doesn't allow to mix raw and generic type easily so
>>>> my students detect the missing angle brackets easily (IntelliJ still doesn't
>>>> warn about missing angle brackets by default :( )
>>>> Now about your example, instead of being functional and wanted each Option to
>>>> type their argument, you can use ugly side effects instead.
>>>> So the idea is to use a temporary class instead of a Map to store the data
>>>> associated with an option. So an Option is something that takes a chunk of the
>>>> command line arguments and do a side effect on the field of an instance of that
>>>> temporary class.
>>> Sure, there might be other ways to get there; what I did, I did it to
>>> test usage of generic enums in a real world code base.
>> it seems to be "the use case" for generics enum.
> I get that you do not like the feature :-)

It's not that i don't like the feature, it's that for me it's a feature you can not even put in the box of the features that we could do. We start with "hey we could do this !" but there are some typing issues. Now, what your are saying is that we can use raw types to not have the typing issues, but as i said above, you are trading an error to a bunch of warnings, doesn't seems to be a good deal*. 

> Other use cases have been discussed here:
> http://mail.openjdk.java.net/pipermail/amber-dev/2017-April/000173.html
> rest assured, the JEP might bear my name, but we're not looking into
> this to make javac code better.

correct me if i'm wrong but your other examples is something that can be replaced by the JVM Constant API.

> Maurizio

* it may sound a little too Trump to your ears, sorry :)

>>> Maurizio
>> Rémi
>>>> public class LineParsing {
>>>>     private final HashMap<String, Consumer<? super Iterator<String>>> actionMap =
>>>>     new HashMap<>();
>>>>     public LineParsing with(String option, Consumer<? super Iterator<String>>
>>>>     action) {
>>>>       actionMap.put(option, action);
>>>>       return this;
>>>>     }
>>>>     public void parse(List<String> args) {
>>>>       var it = args.iterator();
>>>>       while(it.hasNext()) {
>>>>         actionMap.get(it.next()).accept(it);
>>>>       }
>>>>     }
>>>>     public static void main(String[] args) {
>>>>       var bean = new Object() {
>>>>         Path input = Path.of("input.txt");
>>>>         boolean all = false;
>>>>       };
>>>>       new LineParsing()
>>>>           .with("-input", it -> bean.input = Path.of(it.next()))
>>>>           .with("-all", it -> bean.all = true)
>>>>           .parse(List.of(args));
>>>>     }
>>>> }
>>>> regards,
>>>> Rémi
>>>> ----- Mail original -----
>>>>> De: "Maurizio Cimadamore" <maurizio.cimadamore at oracle.com>
>>>>> À: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
>>>>> Envoyé: Mercredi 5 Décembre 2018 17:14:59
>>>>> Objet: enhanced enums - back from the dead?
>>>>> Hi,
>>>>> as mentioned in [1], the work on enhanced enum stopped while ago as we
>>>>> have found some interoperability issues between generic enums and
>>>>> standard enum APIs such as EnumSet/EnumMap.
>>>>> Recently, we have discussed a possible approach that might get us out of
>>>>> the woods, which is described in greater details here:
>>>>> http://cr.openjdk.java.net/~mcimadamore/amber/enhanced-enums.html
>>>>> We have done some internal testing to convince ourselves that, from an
>>>>> operational perspective, where we end up is indeed good. Some external
>>>>> validation might also be very helpful, which is why we're also in the
>>>>> process of releasing the internal patch we have tested internally in the
>>>>> 'enhanced-enums' amber branch (we'll need to polish it a little :-)).
>>>>> Assuming that, usability-wise, our story ticks all the boxes, I think it
>>>>> might be worth discussing a few points:
>>>>> * Do we still like the features described in JEP 301, from an
>>>>> expressiveness point of view?
>>>>> * Both features described in JEP 301 require some sort of massaging. On
>>>>> the one hand sharper typing of enum constants has to take care of binary
>>>>> compatibility of enum constant subclasses into account (for this reason
>>>>> we redefine accessibility of said subclasses along with their binary
>>>>> names). On the other hand, with the newly proposed approach, generic
>>>>> enums also need some language aid (treatment of raw enum constants
>>>>> supertypes). Do we feel that the steps needed in order to accommodate
>>>>> these sharp edges are worth the increase in expressive power delivered
>>>>> by JEP 301?
>>>>> * Our proposed treatment for generic enums raises an additional, more
>>>>> philosophical, question: what are raw types *for* and how happy are we
>>>>> in seeing more of them (in the form of raw enum types)?
>>>>> Cheers
>>>>> Maurizio
>>>>> [1] -
> >>>> http://mail.openjdk.java.net/pipermail/amber-spec-experts/2017-May/000041.html

More information about the amber-spec-experts mailing list