Finalising Enumerations for Scala 3

(The Scala Org aims to release Scala 3 by the end of fall 2020. We are about 15 employees (some of whom work part-time), spread in 4 organisations (+ active community members), focusing on finalising 52 essential projects in 6 months. As of today, project leads will publish the road-maps under the category “Scala 3 release projects” to share with you what is to be expected and hopefully get your advice & contributions as well. All the projects’ road-maps come after an extensive feedback gathering, rounds of discussion, and involvement of major stakeholders, we now need the community to help push this effort over the line. Your collaboration is highly appreciated, thank you in advance!)

The Scala Center team is working on improvements to the enumerations feature before the release of Scala 3, with these main goals in mind:

  • To improve compatibility of enumerations with Java serialisation framework, and Java’s enum APIs when an enumeration extends java.lang.Enum.
  • To provide consistent API’s even as enum definitions grow beyond simple enumerations.
  • To improve intuition behind the desugaring of enum definitions.

Milestone 1: Improve support for Java APIs

  • As it stands in the 0.26.0-RC1 release, if a singleton enum value does not extend java.lang.Enum then it will not remain singleton if it is deserialized using the Java serialization framework. This can be remedied by implementing readResolve to replace the serialized object by one of the known constants.
  • The java.lang.Enum API depends heavily on runtime checking of superclasses of enum values, so it is unsafe to extend java.lang.Enum by more than 2 subclasses deep. When not using migration mode in the compiler, we will restrict extension of java.lang.Enum to only from traits or the enum syntax, as it is done in Java.
  • Enumerations extending java.lang.Enum will be restricted to only be defined in a static object, this is because the Java Language Specification forbids enums which are inner classes.
  • The values array method on the companion object of an enumeration will be sorted by ordinal.

Milestone 2: custom toString for enum

  • As it stands in the 0.26.0-RC1 release, a singleton enum value always overrides toString to be equivalent to the identifier of the enum case. This prevents customisation of toString by the user. We will provide a mechanism for the user to override toString and preserve the accuracy of APIs such as valueOf which looks up values by the string of their identifier.

Milestone 3: Simplification of APIs

  • We will remove scala.runtime.EnumValues in favour of implementing caching and lookup logic directly in the companion object of an enum.
  • We will likely remove the scala.Enum trait while still providing dynamic lookup of the name and ordinal of each enum case. This will make it simpler to extend java.lang.Enum which guarantees your enumeration has only singleton cases.
  • We will standardise the representation of Java style enums in the TASTy format so that it is independent of the compiler.

Milestone 4: Improve Intuition for Enum Desugaring

  • The companion of an enumeration will define public lookup methods (such as values or valueOf) exactly when the enumeration only defines singleton values. It does not matter if the enumeration carries type parameters. If an enum case with a constructor is added, these lookup methods will no longer be generated.
  • Enum cases that do not explicitly provide type parameters will copy the variances of type parameters of the enum that defines them. If an invariant type parameter is required by the constructor, the user will be warned to explicitly provide type parameters for the enum case
  • We will experiment with improving type inference for enumerations, allowing the apply method of enum case companions to give the most precise type.

Acknowledgements

Thank you to Guillaume Raffin for contributing the checks that enforce enum syntax for enumerations extending java.lang.Enum.

Major Changes to the Original SIP

A new change was prompted by the need for an appropriate API to retrieve the identifier of an enum value as a String, where the implementation is restricted to the compiler. In general, it is not a good choice to add a name method, such as in java.lang.Enum, to all enums because that is likely to collide with a name field of a class enum case. To avoid that likely collision, the method name enumLabel was chosen as an instance method for an enum class. As a public API, enumLabel may be adding extra burden when we already have the similar method productPrefix. enumLabel was thought to be more friendly to beginners as a teaching tool, however the need to avoid collisions generates a similarly cryptic name. An alternative is to generate a public method on the companion such as def labelOf(value: E): String where E is an enum type.

As with any of our announcements, we invite your feedback and discussion, as these changes will have to be reviewed by the SIP committee.

15 Likes

Any plans for improving nested / hierarchical enumerations? AFAIK currently enums cannot be nested, so for enum hierarchies one must fall back to sealed traits. This has been discussed on this forum in the enum proposal topic, and was ostensibly punted to be solved somehow in Scala 3.1, however the issue is does not figure in the roadmap above.

2 Likes

The roadmap above only shows what is planned by the release of 3.0. So yes, anything planned for 3.1 would be absent from it.

Hello, I would like to update this forum post to state that in the release of Dotty 0.27.0-RC1, we
released all of Milestone 2 and all but one points of Milestone 1, and the nightly 0.28.0-bin-20200902-95a6b44-NIGHTLY completes Milestone 1 by restricting enums extending java.lang.Enum to static scopes. We are currently working on Milestones 3 and 4.

3 Likes