Enumeration must DIE...

7622 views
Skip to first unread message

Som Snytt

unread,
Oct 19, 2012, 8:00:29 PM10/19/12
to scala-internals
...where DIE = Divergent Implicit Expansion.

While looking at the DIE issue with mapping Enumeration values
https://issues.scala-lang.org/browse/SI-5534

I noticed that one quick fix is to make Value no longer Ordered.

My question is whether the "id" or "ordinal" for Enumeration.Values should be internal detail.  By extension, we can remove Ordered.  I moved the Ordering[Value] to a related companion class.

As an example, Reporter.Severity is a Value used just as an int wrapper. I had to add Ordered back to Severity for INFO < ERROR tests.

I reread the DIE page of the spec, combed through the -Yinfer-debug, and more/less understand what's intended by the newCBF for SortedSet.  However, my understanding is only 66% so far on the implicit problem, so I don't want to suggest that this is the optimal fix.

Also of interest, ValueSet uses an underlying BitSet, so you lose a bit of range:
https://issues.scala-lang.org/browse/SI-6094
but probably ValueSet doesn't want to create sparse BitSets with huge ranges anyway and should switch to a different representation after a relatively small topId - bottomId.

If ids were only ordinals, with bottomId always zero, the conceptual model would be as simple as it really is.

Simon Ochsenreither

unread,
Oct 20, 2012, 6:03:11 PM10/20/12
to scala-i...@googlegroups.com
scala.Enumerations have caused nothing but trouble as far as I can think back ... I wonder when it is finally enough and Scala will just use the JVM's/Java's enums. Maybe macros will get us a lot closer to some sane, interoperable solution by synthesizing the appropriate methods, just like javac does.

Som Snytt

unread,
Oct 20, 2012, 10:51:13 PM10/20/12
to scala-i...@googlegroups.com
On Sat, Oct 20, 2012 at 3:03 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
scala.Enumerations have caused nothing but trouble as far as I can think back ... I wonder when it is finally enough and Scala will just use the JVM's/Java's enums. Maybe macros will get us a lot closer to some sane, interoperable solution by synthesizing the appropriate methods, just like javac does.

I should add that while I understand this point of view, in the subject line I was only being flippant (which I hope has nothing to do with flipping off, but one can't be sure about French terms).  Or maybe the word is glib.  Let's say, flib, which is a flub that went over the top.

I actually like Enumeration as a showcase module.
http://www.artima.com/pins1ed/abstract-members.html#20.8

It's been hard to get exactly right, but only in a good way.  The last issue was, "Enum doesn't know how to compare two ints."  Enumeration really deserves its own section on Scala Puzzlers.  As the other thread on enums and case objects shows, there should be a whole other section on enum alternatives.

I was kind of surprised that paulp hasn't taken a big hammer to Enumeration, but I just checked the history and there's a "Fix for Enumeration" followed shortly thereafter by a 'Revert "Fix for Enumeration".'   Just because a task is difficult and thankless, doesn't mean it's not worth trying.

In the current issue, the set of values became a sorted set, and the code was made to imitate BitSet (which it happens to use under the hood).  BitSet works nicely as a SortedSet[Int], but ValueSet as SortedSet[Value] demonstrates this glitch.  (Because Value is Ordered, and Pair[Ordered, _] picks up the implicit that seems to DIE.)

For me, the motivation is to understand implicits, the Ordering apparatus, and how to extend collections.  I think that may rate a puzzler.


martin odersky

unread,
Oct 21, 2012, 4:10:18 AM10/21/12
to scala-i...@googlegroups.com


On Sun, Oct 21, 2012 at 12:03 AM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
scala.Enumerations have caused nothing but trouble as far as I can think back ... I wonder when it is finally enough and Scala will just use the JVM's/Java's enums. Maybe macros will get us a lot closer to some sane, interoperable solution by synthesizing the appropriate methods, just like javac does.

You can use Java enums. But Scala will never have them. 

I have to repeat my mantra once more here: My intention is to make Scala a simpler language, with fewer features, not more. Most of the discussion on the Scala lists goes the other way, where everyone is rooting for just one more essential feature. That's only natural. But I have by now developed a high level of resistance to most of these proposals.

Cheers

 - Martin


Simon Ochsenreither

unread,
Oct 21, 2012, 11:15:58 AM10/21/12
to scala-i...@googlegroups.com
Hi Martin,

I think we are in perfect agreement on that!

I just assumed that Scala absolutely needed some native, Java-enum-like replacement. If that's not the case and using Java for this purpose is considered fine, forget about my considerations on how to implement Java-compatible enums in Scala without introducing new features.

Let's deprecate scala.Enumeration in 2.11 and guide people either towards Java's enums or Scala's ADT-like approach.

One feature less, less bugs, less confusion on what to use (see https://groups.google.com/d/topic/scala-user/2g-qzsfE0Us/discussion, https://groups.google.com/d/topic/scala-user/2ZiLW92ieq4/discussion), better interop and no need to duplicate APIs for Java/Scala enums. (Considering the usage on scala.Enumeration in the wild, it seems people already voted with their feet, too.)

Thanks and bye,

Simon

Paul Phillips

unread,
Oct 21, 2012, 11:16:03 AM10/21/12
to scala-i...@googlegroups.com


On Sun, Oct 21, 2012 at 1:10 AM, martin odersky <martin....@epfl.ch> wrote:
I have to repeat my mantra once more here: My intention is to make Scala a simpler language, with fewer features, not more. Most of the discussion on the Scala lists goes the other way, where everyone is rooting for just one more essential feature. That's only natural. But I have by now developed a high level of resistance to most of these proposals.

This seems like a questionable example of people rooting for one more feature; the primary thing being rooted for when it comes to scala.Enumeration is its removal. I thought leveraging java's existing and satisfactory solutions where possible was scala's thing.

Ismael Juma

unread,
Oct 21, 2012, 12:01:59 PM10/21/12
to scala-i...@googlegroups.com
On Sun, Oct 21, 2012 at 4:16 PM, Paul Phillips <pa...@improving.org> wrote:
This seems like a questionable example of people rooting for one more feature; the primary thing being rooted for when it comes to scala.Enumeration is its removal. I thought leveraging java's existing and satisfactory solutions where possible was scala's thing.

Particularly since 2.10 is meant to include exhaustiveness checks for Java enums too.

Best,
Ismael 

martin odersky

unread,
Oct 21, 2012, 3:05:45 PM10/21/12
to scala-i...@googlegroups.com
I think Enumeration works fine for what it's supposed to do: Provide enumerations without the overhead of one class per value. It beats the alternative of defining numeric constants, for sure.

Cheers

 - Martin



Paul Phillips

unread,
Oct 21, 2012, 3:09:41 PM10/21/12
to scala-i...@googlegroups.com


On Sun, Oct 21, 2012 at 12:05 PM, martin odersky <martin....@epfl.ch> wrote:
Provide enumerations without the overhead of one class per value.

Two classes per value, usually, not that case objects are the only imaginable alternative.  But "works fine" is one of those subjective things.  I like my scala features to work at least as well as the java feature they are reimplementing.  By that standard it does not rise to the level of fine.

martin odersky

unread,
Oct 21, 2012, 3:11:24 PM10/21/12
to scala-i...@googlegroups.com
Yes, but it also has a whole lot less language footprint (to be exact: zero). Enumerations in Java are quite enormous, similar in complexity to, say, traits in Scala.  Is the convenience really worth it?

Cheers

 - Martin



Simon Ochsenreither

unread,
Oct 21, 2012, 3:32:42 PM10/21/12
to scala-i...@googlegroups.com
Enumerations in Java are quite enormous, similar in complexity to, say, traits in Scala.  Is the convenience really worth it?

As far as I have understood it, people in this thread are arguing to get rid of scala's bug-ridden, non-interoperable implementation in favor of just using Java's enums, not about changing Scala-the-language to add anything to it.

The convenience of not having to deal with scala.Enumeration and the poor souls who fell into the trap using it has no impact on the language footprint, but imho a lot of positive impact on people learning Scala.

Rex Kerr

unread,
Oct 21, 2012, 4:11:15 PM10/21/12
to scala-i...@googlegroups.com
On Sun, Oct 21, 2012 at 3:05 PM, martin odersky <martin....@epfl.ch> wrote:

I think Enumeration works fine for what it's supposed to do: Provide enumerations without the overhead of one
class per value. It beats the alternative of defining numeric constants, for sure.

Well, occasionally I suppose it does.  It's integer-only (numeric constants are not), boxed (numeric constants are not), and allows (at compile-time) but breaks on repeated values (numeric constants allow repeats and do not break):

    object Wrong extends Enumeration { val a,b = Value(2) }

It can't check for complete pattern matches (unlike the case where classes are used), and admits various weird usages that fail at runtime:

    object Okay extends Enumeration { val c = Value }
    object Uhoh extends Enumeration { val d = Okay.Value }

And although it doesn't add to the _language_ complexity, it does add to the _library_ complexity.  It's amazing that it works at all, but even so it doesn't work very well.

In practice, between needing byte and short and floating point constants, and needing pattern matching, and needing bitmasks for which Enumeration generates the wrong progression, I hardly use it at all.

I would use it more if it was something like

class Incrementable[T](val zero: T, val inc: T => T) {}
abstract class Enumeration[T: Incrementable] { ... }

  --Rex

Erik Osheim

unread,
Oct 21, 2012, 4:18:25 PM10/21/12
to scala-i...@googlegroups.com
On Sun, Oct 21, 2012 at 04:11:15PM -0400, Rex Kerr wrote:
> In practice, between needing byte and short and floating point constants,
> and needing pattern matching, and needing bitmasks for which Enumeration
> generates the wrong progression, I hardly use it at all.

Yeah, that's been my experience as well. Ugly-but-verbose numeric
constants are reliable, represent the (unboxed) values and types I
need, and be be easily inlined if that's what I want.

-- Erik

martin odersky

unread,
Oct 21, 2012, 4:22:26 PM10/21/12
to scala-i...@googlegroups.com
On Sun, Oct 21, 2012 at 10:11 PM, Rex Kerr <ich...@gmail.com> wrote:
On Sun, Oct 21, 2012 at 3:05 PM, martin odersky <martin....@epfl.ch> wrote:

I think Enumeration works fine for what it's supposed to do: Provide enumerations without the overhead of one
class per value. It beats the alternative of defining numeric constants, for sure.

Well, occasionally I suppose it does.  It's integer-only (numeric constants are not), boxed (numeric constants are not), and allows (at compile-time) but breaks on repeated values (numeric constants allow repeats and do not break):

    object Wrong extends Enumeration { val a,b = Value(2) }

It can't check for complete pattern matches (unlike the case where classes are used), and admits various weird usages that fail at runtime:

    object Okay extends Enumeration { val c = Value }
    object Uhoh extends Enumeration { val d = Okay.Value }

Enumeration comes with a usage pattern. If you use it wrongly, it breaks. I do not see much that's wrong with that. If you use the wrong numeric constants (ie the same one twice) your program will break as well. And that is usually harder to guard against than just writing

  val a, b, c = Value

To each their own. People who do not like enumeration do not need to use it. Sometimes numeric constants are better. But if you do not want to watch out for duplicate values manually, 
and you do not need utmost performance, enumerations are nice.

Cheers

 - Martin


And although it doesn't add to the _language_ complexity, it does add to the _library_ complexity.  It's amazing that it works at all, but even so it doesn't work very well.

In practice, between needing byte and short and floating point constants, and needing pattern matching, and needing bitmasks for which Enumeration generates the wrong progression, I hardly use it at all.

I would use it more if it was something like

class Incrementable[T](val zero: T, val inc: T => T) {}
abstract class Enumeration[T: Incrementable] { ... }

Note that java enums only map to ints as well.

But to 

 


  --Rex




--
Martin Odersky
Prof., EPFL and Chairman, Typesafe
PSED, 1015 Lausanne, Switzerland
Tel. EPFL: +41 21 693 6863
Tel. Typesafe: +41 21 691 4967

Simon Ochsenreither

unread,
Oct 21, 2012, 5:38:51 PM10/21/12
to scala-i...@googlegroups.com
Hi,


Enumeration comes with a usage pattern. If you use it wrongly, it breaks. I do not see much that's wrong with that.
I honestly can't remember scala.Enumeration's "right" usage without looking it every time I considered using it. That alones leaves a very bad taste.
Java managed to completely prevent this problem of "wrong usage patterns". To this day, I haven't seen a definition of Java's enum which both compiled _and_ broke at runtime.
Why would we consider less than this acceptable for Scala?

If you use the wrong numeric constants (ie the same one twice) your program will break as well. And that is usually harder to guard against than just writing
The issue, as far as I have understood it, is that people actually choose to go back to some less expressive and less semantically exact code instead of using Scala's Enumeration.
The only "static final int"s I see in Java are pre-enums. It seems that Scala's Enumeration didn't even manage to solve this basic requirement in a way people would actually want to use it.

To each their own. People who do not like enumeration do not need to use it.
Sometimes numeric constants are better. But if you do not want to watch out for duplicate values manually, 
and you do not need utmost performance, enumerations are nice.
With type aliases and value types there are even more options for safe, declarative code, but I think that's besides the point.
scala.Enumerations doesn't seem to solve any actual issue and has all the drawbacks of not being a java.lang.Enum (like in annotations).
People who want to use enumerations should use Java enums, they don't deserve that we waste their time with scala.Enumeration. So can we get rid of scala.Enumeration, please?

Thanks and bye,

Simon


Rex Kerr

unread,
Oct 21, 2012, 5:44:20 PM10/21/12
to scala-i...@googlegroups.com

Can we try rewriting it first, taking advantage of the huge increase in experience with writing reliable Scala library code and experience with the difficulties of the existing version?

Or maybe wait until we have a proper units system--if we're going to get one--so that we can leverage that for type-safety?

  --Rex

Simon Ochsenreither

unread,
Oct 21, 2012, 6:04:29 PM10/21/12
to scala-i...@googlegroups.com

Can we try rewriting it first, taking advantage of the huge increase in experience with writing reliable Scala library code and experience with the difficulties of the existing version?
That's what I proposed in the first comment, but that was shot down. Also, I don't see how we could make scala.Enumeration a Java enum without tons of macro magic. In the end, it would be more like some Scala-like DSL where the macro would completely rewrite the tree of the "enum declaration". This couldn't be done with method-level macros, we would probably need macro types or macro annotations for that.

Rex Kerr

unread,
Oct 21, 2012, 6:26:06 PM10/21/12
to scala-i...@googlegroups.com
Well, you proposed a specific rewrite for it, which was basically to use macros to mimic Java enums, which was shot down.  Even without going that far, one could make some significant improvements--not to Java interop, but to robustness, flexibility, speed, etc. (not all at once--one would need to decide what to target).

  --Rex

Simon Ochsenreither

unread,
Oct 21, 2012, 6:56:10 PM10/21/12
to scala-i...@googlegroups.com
If the rewritten scala.Enumeration isn't compatible with Java enums, why even bother rewriting it? I prefer a useless implementation with known bugs to a useless implementation with unknown bugs.
There is a reason why practically no-one uses scala.Enumeration and "incompatible with the platform' idea of enums" and "buggy" rank pretty high on my list of suspects.

Nothing against a bit of NIH, but there is only one valid definition of "enum" and that's the one of the JVM. If the JVM tells me "no, that's not an enum, I won't allow you to use this in an annotation" I don't care about Scala's reason for failing to work as expected.

Thanks and bye,

Simon

Rex Kerr

unread,
Oct 21, 2012, 7:34:17 PM10/21/12
to scala-i...@googlegroups.com
On Sun, Oct 21, 2012 at 6:56 PM, Simon Ochsenreither <simon.och...@gmail.com> wrote:
If the rewritten scala.Enumeration isn't compatible with Java enums, why even bother rewriting it? I prefer a useless implementation with known bugs to a useless implementation with unknown bugs.
There is a reason why practically no-one uses scala.Enumeration and "incompatible with the platform' idea of enums" and "buggy" rank pretty high on my list of suspects.

So you don't think it's possible to write less buggy/non buggy code?  Why write anything?

Okay, so, kind of a straw man--I take it that you mean that platform compatibility is an essential requirement for you.
 
Nothing against a bit of NIH, but there is only one valid definition of "enum" and that's the one of the JVM. If the JVM tells me "no, that's not an enum, I won't allow you to use this in an annotation" I don't care about Scala's reason for failing to work as expected.

Reinforced by this point.  If you want Java enums, using Java is a pretty sensible option.  But I don't personally miss being able to stick a Scala Enumeration in a Java annotation.  >90% of my use cases is not creating new annotations.  Unsafe numbering, namespace pollution, ints-only (see classic java "Planets" example for what you can't do in Scala), etc. each chip off a big fraction for me.  I'd be happy to have something better (c.f. "The great is the enemy of the good.").

  --Rex

Simon Ochsenreither

unread,
Oct 21, 2012, 8:09:12 PM10/21/12
to scala-i...@googlegroups.com

If the rewritten scala.Enumeration isn't compatible with Java enums, why even bother rewriting it? I prefer a useless implementation with known bugs to a useless implementation with unknown bugs.
There is a reason why practically no-one uses scala.Enumeration and "incompatible with the platform' idea of enums" and "buggy" rank pretty high on my list of suspects.
So you don't think it's possible to write less buggy/non buggy code?  Why write anything?
My point is that given the constraints it is obvious (imho) that scala.Enumeration can't be fixed to justify its existence, not even remotely.
 
Okay, so, kind of a straw man--I take it that you mean that platform compatibility is an essential requirement for you.
What's the value added by not being compatible with the platform's idea of enums?
 
Nothing against a bit of NIH, but there is only one valid definition of "enum" and that's the one of the JVM. If the JVM tells me "no, that's not an enum, I won't allow you to use this in an annotation" I don't care about Scala's reason for failing to work as expected.
Reinforced by this point.  If you want Java enums, using Java is a pretty sensible option.  But I don't personally miss being able to stick a Scala Enumeration in a Java annotation.  >90% of my use cases is not creating new annotations.
I certainly don't miss scala.Enumeration. It does neither anything well nor anything better than Java enums. It's usage is non-obvious and confusing.

Unsafe numbering, namespace pollution, ints-only (see classic java "Planets" example for what you can't do in Scala), etc. each chip off a big fraction for me.  I'd be happy to have something better (c.f. "The great is the enemy of the good.").
Which of these points is not already adressed by Java enums? How is some, potentially future version of scala.Enumeration improving on what Java's enum did reliably and painfree for the last 8 years? Why or who would want to wait for it at all?
I think the fact that people abandoned scala.Enumeration in favor of integers and fields speaks lengths about the situation. Why not put an end to this suffering, get rid of Scala.Enumeration and give an official blessing to Java enums?

Paul Phillips

unread,
Oct 21, 2012, 9:26:23 PM10/21/12
to scala-i...@googlegroups.com

On Sun, Oct 21, 2012 at 4:34 PM, Rex Kerr <ich...@gmail.com> wrote:
Unsafe numbering, namespace pollution, ints-only

Every scala.Enumeration value having the same erasure, which for most people is a discovery not to be made until after they've boxed themselves in.

scala> object Foo extends scala.Enumeration { val A, B, C = Value }
defined module Foo

scala> object Bar extends scala.Enumeration { val A, B, C = Value }
defined module Bar

scala> def f(x: Foo.Value) = 1 ; def f(x: Bar.Value) = 1
<console>:9: error: double definition:
method f:(x: Bar.Value)Int and
method f:(x: Foo.Value)Int at line 9
have same type after erasure: (x: Enumeration#Value)Int
       def f(x: Foo.Value) = 1 ; def f(x: Bar.Value) = 1
                                     ^

Oleg Galako

unread,
Oct 22, 2012, 2:18:29 AM10/22/12
to scala-i...@googlegroups.com
I think the answer to this will one day come as an independent library (maybe using macros) which will be used by the community. Something like Akka for actors.

Jan Vanek

unread,
Oct 22, 2012, 5:32:58 AM10/22/12
to scala-i...@googlegroups.com
In our project we need to translate the enums - provide texts for the values. In Java what we have to do is to add an extra line into enums, like: 

public enum AddressStatus implements EnumClass<AddressStatus>
{
    ...
    public static final EnumMetaClass<AddressStatus> meta = EnumMetaClass.create(AddressStatus.class, "i18n.app");
}

This is something what can be done much better in Scala. You can't easily add functionality to Java enums. So, to say "just use Java enums" is not a good solution.

Second, we need to customize the enumerated value (add fields, not just one string). This is something you can't do with scala.enum, but with java enum you at least have to write the fields + constructor (assign fields). In Scala this would look nicer.

Third, we don't need complex numbering, built-in ordinal as in Java enum is just fine. Rest is done with fields - constructor params.

So, I took the enum from V. Klang and adapted it to our needs. I think, in the meantime, there exist dozens of house-local enums out there.

My point is, I think there is a need for Scala enum, just not the current one. I mean enum as library, not language built-in.

Regards,
Jan

Josh Suereth

unread,
Oct 22, 2012, 5:39:38 AM10/22/12
to scala-internals

I'd like to chime in on the anti- scala.Enumeration camp.

My point is slightly altered from others:  scala.Enumeration doesn't feel like it belongs in Scala.

If you look at all the other instances of "just a library" for scala, like beakables, actors, futures then you see a nice, elegant abstraction that solves a domain well, is performant at runtime, and "fits" into the language as if it were native.

Enumeration is not.  If you look at its usage pattern it is different from any other thing in the standard library, violates many of the common scala idioms.   While enumerations tend to do this in any language, I think those in Scala's library are a good idea gone poor.

I believe Viktor Klang had a toy replacement that was far more *runtime* safe and performant and unified with case classes.  This I could get behind as more "regular", adding features that feel more natural.   Placing a sealed set of case objects in another object is not as foreign as calling a `Value` method to create one or more instances.  During training we often have to wave our hands here and encourage folks to investigate later.

Here's the crux of the complaint against Enumeration:

(1) toString is not the common way of pulling *data* out of an object (I.e. the name member).
(2) Cannot easily extend enumerated type with additional members.  All new behavior in the containing object.
(3) Implementation practically requires you to use it as top level object, not inside a class or trait.
(4) Does not feel like a set of case object/classes, the major pattern in scala used with pattern matching.

Basically, enumeration always feels like something unnatural, both as a library in the standard, and something the language would support.

I don't even care about java interop here.   I'm not about to say we need compiler support.   However I think the current library is lacking.   If you would entertain improvements, I think we could make them.

If the main goal was avoiding more classes, then perhaps we've succeeded.   However, when I have an ich I want to scratch with enumeration, the current library is a thorny branch, when I just want a nice stick.   I seriously think we should consider improvement here.

Stefan Zeiger

unread,
Oct 22, 2012, 6:55:52 AM10/22/12
to scala-i...@googlegroups.com
On 2012-10-21 22:11, Rex Kerr wrote:
> In practice, between needing byte and short and floating point
> constants, and needing pattern matching, and needing bitmasks for
> which Enumeration generates the wrong progression, I hardly use it at all.

Care to elaborate about the bitmasks? That was one of the main
motivations for switching to the BitSet-based implementation in 2.10.

-sz

martin odersky

unread,
Oct 22, 2012, 8:20:37 AM10/22/12
to scala-i...@googlegroups.com
I am happy to see improvements to enum, of course, and if it requires a different design, that's fine.

There's just one caveat: I really think that enums should be lightweight. One class (or even two) per value is not acceptable. If you are willing to pay that sort of price, it's not too burdensome to just define the case objects directly. Enums fill a different niche: essentially as efficient as integer constants but safer and more convenient to define and to use.

If someone has a design how to make this work better, great!

Cheers

 - Martin

Josh Suereth

unread,
Oct 22, 2012, 8:33:27 AM10/22/12
to scala-i...@googlegroups.com
Sounds like a plan.  I don't think the goal of having minimal enumeration is bad, unless it locks us from improving it.  We have a lot of new vectors of attack we can use, from value classes to macros, and I think Enumeration is ripe for improvement.  If we wall understand the goals:

  • Lightweight, where lightweight = # of classes generated.
  • Typesafe, where one enumeration cannot be passed to another
  • Associates integer id w/ name
I think we could end up with a more "scala" enumeration class given those constraints.

Eugene Burmako

unread,
Oct 22, 2012, 9:03:22 AM10/22/12
to scala-i...@googlegroups.com

I think we can make Enumeration a trait macro, given we empower trait macros to read contents of host classes

On Oct 22, 2012 3:33 PM, "Josh Suereth" <joshua....@gmail.com> wrote:
Sounds like a plan.  I don't think the goal of having minimal enumeration is bad, unless it locks us from improving it.  We have a lot of new vectors of attack we can use, from value classes to macros, and I think Enumeration is ripe for improvement.  If we wall understand the goals:

  • Lightweight, where lightweight = # of classes generated.
  • Typesafe, where one enumeration cannot be passed to another
  • Associates integer id w/ name
I think we could end up with a more "scala" enumeration class given those constraints.



On Mon, Oct 22, 2012 at 8:20 AM, martin odersky <martin....@epfl.ch> wrote:
>

> I am happy to ...

Jan Vanek

unread,
Oct 22, 2012, 9:24:57 AM10/22/12
to scala-i...@googlegroups.com
On Mon, Oct 22, 2012 at 3:03 PM, Eugene Burmako <eugene....@epfl.ch> wrote:

I think we can make Enumeration a trait macro, given we empower trait macros to read contents of host classes

I also thought type macros would be necessary in order to define the fields. Those are not available afaik. However, I thought about how to define the values so that those definition could be seen by the type-macro and perhaps it could use dynamic (likely just dummy to make it compile) calls like this:

object A extend Enum {
  class Value(p1: String, p2: Double)

  // those are dynamic calls:
  A("1", 1)
  B("2", 2)
}

Or do you have a better way in mind?

Regards,
Jan

Eugene Burmako

unread,
Oct 22, 2012, 9:27:19 AM10/22/12
to scala-i...@googlegroups.com
Tbh I don't have any specific encoding of enums for Scala, just proposed type/trait macros as an implementation vehicle, since they might arrive soon.

Speaking of your example, what does A("1", 1) mean?

Jan Vanek

unread,
Oct 22, 2012, 9:33:08 AM10/22/12
to scala-i...@googlegroups.com
A("1", 1) serves to define a value A - instance of Value with p1="1" and p2=2. This "definition" would happen in the type macro, i.e. the result will be:

val A = new Value("1", 1)

But type-macro (I think, I can't know) needs some information to generate such definition, and I thought it could be provided as indicated, where the A("1", 1) would technically be a dynamic call - so that it compiles (well, of course if there is other way to make it compile, then it's better, only I don't know any at the moment).

Regards,
Jan

Eugene Burmako

unread,
Oct 22, 2012, 9:36:18 AM10/22/12
to scala-i...@googlegroups.com
One of the possibilities would be having type macros operating on untyped trees, therefore in thinking about an encoding for enums I wouldn't be too restrained by making stuff typecheck as is. If there's a nice idea that is untyped, we might be able to make it work.

Simon Ochsenreither

unread,
Oct 22, 2012, 9:36:39 AM10/22/12
to scala-i...@googlegroups.com

Tbh I don't have any specific encoding of enums for Scala, just proposed type/trait macros as an implementation vehicle, since they might arrive soon.
 
That's what I proposed, but I think this was shot down already?

Having these means, more or less everything could be done, including inheriting rom java.lang.Enum _and_ allowing people to mix in additional functionality (something you loose when using Java's enums).

But on the other hand, this would amount to having a mini-DSL for enums, because more or less the whole tree would need to be rewritten by the macro.

Simon Ochsenreither

unread,
Oct 22, 2012, 9:40:46 AM10/22/12
to scala-i...@googlegroups.com
I think this would be a good test-case to measure an improvement proposal:

public enum Operation {
  PLUS   { double eval(double x, double y) { return x + y; } },
  MINUS  { double eval(double x, double y) { return x - y; } },
  TIMES  { double eval(double x, double y) { return x * y; } },
  DIVIDE { double eval(double x, double y) { return x / y; } };

  abstract double eval(double x, double y);
}

http://docs.oracle.com/javase/1.5.0/docs/guide/language/enums.html

If someone would manage to make something akin to this work, I would consider some native-Scala enum again.
Until that happens, let's get rid of scala.Enumeration.

Matthew Pocock

unread,
Oct 22, 2012, 10:34:48 AM10/22/12
to scala-i...@googlegroups.com
I have never managed to successfully use scala's enums. I've either ended up reaching for Java enums or fully-fledged sealed traits with case objects. Every time I've tried to use them, I've ended up needing to go back to tutorials and documentation, and fairly quickly found that they don't do what I want, or that every time I refactor things, I have to make a catastrophic change into or out from enums.

Consider the heavy-weight alternative:

sealed trait CompassPoint

object CompassPoint {
  case object North extends CompassPoint
  case object South extends CompassPoint
  case object East extends CompassPoint
  case object West extends CompassPoint
}

This usually gives me more than I need - I don't need North.type and friends, just CompassPoint.North as a unique entity of the same type as South and its other siblings. But, at least it's sealed and scoped, the constants can't be subclassed and are immutable singletons. It's heavy-weight in the amount of code generated. Slightly more light-weight is:

object CompassPoint {
  val North = new CompassPoint
  val South = new CompassPoint
  val East = new CompassPoint
  val West = new CompassPoint
}

However, we need extra boilerplate here so that names work out, and it's easy to do this wrong so that the instances aren't distinguishable according to .hashCode and .equal. In both cases I've had to write separate trait and object, and repeat the trait name all over the place. Eugh!

I guess the point I'm trying to make, in a rambly, unfocussed way, is that I consider scala's enumerations to be unusuable and confusing to the point that I could never recommend that it be used, and would like an alternative that feels like normal scala case class/object hierarchies but with a concise syntax. How about something like:

object CompassPoint {
  case val North
  case val Sourth
  case val East
  case val West
}

This would produce a synthetic, sealed trait CompassPoint, and each case val would be an instance of this. I'm suggesting this as a strawman. Feel free to sprinkle annotations. We have case classes and case objects. For enumerations, it smells like we are groping towards case vals.

Matthew
--
Dr Matthew Pocock
Integrative Bioinformatics Group, School of Computing Science, Newcastle University
skype: matthew.pocock
tel: (0191) 2566550

√iktor Ҡlang

unread,
Oct 22, 2012, 10:37:36 AM10/22/12
to scala-i...@googlegroups.com
This works:

object CompassPoint { val North, South, East, West = new CompassPoint }
--
Viktor Klang

Akka Tech Lead
Typesafe - The software stack for applications that scale

Twitter: @viktorklang

Paul Phillips

unread,
Oct 22, 2012, 10:39:27 AM10/22/12
to scala-i...@googlegroups.com


On Mon, Oct 22, 2012 at 7:34 AM, Matthew Pocock <turingate...@gmail.com> wrote:
object CompassPoint {
  val North = new CompassPoint
  val South = new CompassPoint
  val East = new CompassPoint
  val West = new CompassPoint
}

However, we need extra boilerplate here so that names work out, and it's easy to do this wrong so that the instances aren't distinguishable according to .hashCode and .equal. In both cases I've had to write separate trait and object, and repeat the trait name all over the place. Eugh!

Nils Kilden-Pedersen

unread,
Oct 22, 2012, 11:54:12 AM10/22/12
to scala-i...@googlegroups.com
On Mon, Oct 22, 2012 at 4:32 AM, Jan Vanek <j3v...@gmail.com> wrote:
You can't easily add functionality to Java enums.

Sure you can, you can add any method to enum and any abstract method to your enum definition and have the enum values provide an implementation.

 

Jan Vanek

unread,
Oct 22, 2012, 12:08:30 PM10/22/12
to scala-i...@googlegroups.com
Yes, you can do that. What I meant is to add a shared functionality into multiple of enum classes, like e.g. support for translation. To do that nicely requires extending a "base enum" class from which your concrete enums will inherit. But you can't extend enums in Java.

Regards,
Jan

Jan Vanek

unread,
Oct 22, 2012, 12:46:22 PM10/22/12
to scala-i...@googlegroups.com
OK, I imagine something like this:

In Scala library there something like this (I only write the interface), I don't know how the macro type will look like:

  trait Enumeration extends Serializable {
    type Value <: Enumerated

    trait Enumerated extends Serializable {
      val ordinal: Int
      val name: String
    }

    def values: Seq[Value]
  }

Someone might need to customize the enums, so he/she writes this:

  trait MyEnumeration(resourceBundle: String) extends Enumeration {
    type Value <: Enumerated

    trait Enumerated extends super.Enumerated {
      def text = "get from resource bundle"
    }
  }

Concrete enums are always objects. Example 1:

  object Status extends MyEnumeration("i18n/xy") {
    // generated
    // type Value = Status

    // generated
    // private class ValueImpl(val ordinal: Int, val name: String) extends Value

    RUNNING
    FINISHED

    // generated
    // val RUNNING: Value = new ValueImpl(0, "RUNNING")
    // val FINISHED: Value = new ValueImpl(1, "FINISHED")
  }

  // generated
  // abstract class Status extends Status.Enumerated

Simon's example which however requires a class per value.

  object Operation extends Enumeration {
    // generated
    // type Value = Operation

    // generated
    // private abstract class ValueImpl(val ordinal: Int, val name: String) extends Value

    PLUS { def eval(x: Double, y: Double) = x + y }

    // generated:
    // val PLUS: Value = new ValueImpl(0, "PLUS") { def eval(x: Double, y: Double) = x + y }
  }

  // not generated, have to write

  trait Operation extends Operation.Enumerated {
    def eval(x: Double, y: Double): Double
  }

I don't know whether macro types could generate "companion" classes. Also, the macro should "trigger" only in object, not in the custom MyEnumeration. This might not work, you know better.

Also, I didn't insert the values into the values collection in base Enumerated, but that should be doable. Also, I didn't attempt to implement the serialization stuff, like writeReplace/readResolve - however it has been done and once I did a modification to account for whether the enum is top-level object or not.

Regards,
Jan

Rex Kerr

unread,
Oct 22, 2012, 1:13:20 PM10/22/12
to scala-i...@googlegroups.com

val ForwardFlag = 0x01
val OccupiedFlag = 0x02
val UpdatedFlag = 0x04
val ArrayBackedFlag = 0x08
val ExtraFlag = 0x10
...

vs

object Flags extends Enumeration { ??? }

  --Rex

Jan Vanek

unread,
Oct 22, 2012, 2:11:23 PM10/22/12
to scala-i...@googlegroups.com
object Flag extends Enumeration {
  Forward
  Occupied
  Updated
  ArrayBacked
  Extra
}

trait Flag extends Flag.Enumerated {
  val value = 1 << ordinal
}

Is this too heavyweight for your requirements?

With regards,
Jan

P.S. Sorry about mentioning that we don't need complex numbering in our project.

Som Snytt

unread,
Oct 22, 2012, 2:33:22 PM10/22/12
to scala-i...@googlegroups.com
Perhaps this isn't -internals fodder anymore, and I apologize for opening this can of worms, which turned out to be one of those party favors from which a huge snake springs and causes a big ruckus.

I was curious about the bit mask use case, so here is my finger exercise.  No DIE.

For the record, paulp is the reason I align my assignment operators (after all these years).  Scala has brought so much good into the world.

package mixer

import scala.language.implicitConversions

object colors extends Enumeration {
  trait Color {
    def bits: Int
    def isPrimary: Boolean
  }
  implicit def asColor(v: Value) = v.asInstanceOf[Color]
  implicit class Primarily(val mask: Array[Long]) extends AnyVal {
    def toPrimaryMask: Int = {
      val ps = colors.values filter (_.isPrimary)
      val pbits = ps.toBitMask(0).toInt
      mask(0).toInt & pbits
    }
  }
  class Primary extends Val with Color {
    def bits = ValueSet(this).toBitMask.toPrimaryMask
    def isPrimary = true
  }
  class Secondary(p1: Primary, p2: Primary) extends Val with Color {
    def bits = (p1 + p2).toBitMask.toPrimaryMask
    def isPrimary = false
  }
  val Red, Blue = new Primary
  val Purple    = new Secondary(Red, Blue)    // in this position, consumes a bit
  val Yellow    = new Primary
  val Green     = new Secondary(Blue, Yellow)
}

object Test extends App {
  import colors._
  println(s"${Red.bits} are red, ${Blue.bits} are blue")
  println(s"${Purple.bits} are purple, and so are you")
  println(s"Luck of the Irish, wearin' o' the green: ${Green.bits}")

Nils Kilden-Pedersen

unread,
Oct 22, 2012, 3:33:21 PM10/22/12
to scala-i...@googlegroups.com
On Mon, Oct 22, 2012 at 1:11 PM, Jan Vanek <j3v...@gmail.com> wrote:
object Flag extends Enumeration {
  Forward
  Occupied
  Updated
  ArrayBacked
  Extra
}

trait Flag extends Flag.Enumerated {
  val value = 1 << ordinal
}

What happens when you run out of bits, which doesn't take long?

Eugene Burmako

unread,
Oct 22, 2012, 4:44:42 PM10/22/12
to scala-i...@googlegroups.com
Nice pun :)

Jan Vanek

unread,
Oct 22, 2012, 4:56:54 PM10/22/12
to scala-i...@googlegroups.com
On 22.10.2012 20:33, Som Snytt wrote:
Perhaps this isn't -internals fodder anymore, and I apologize for opening this can of worms, which turned out to be one of those party favors from which a huge snake springs and causes a big ruckus.

I was curious about the bit mask use case, so here is my finger exercise.  No DIE.

You're right, my scheme can't encode two/more internal sub-classes of such an enum. Two/more constructors is possible, but that is not good enough for you. Something better is needed.

Rex Kerr

unread,
Oct 22, 2012, 5:24:40 PM10/22/12
to scala-i...@googlegroups.com
Er, this is some weird hybrid of Java enums and Scala Enumeration.  That might be okay, but it's not available now.

  --Rex

Havoc Pennington

unread,
Oct 22, 2012, 5:45:00 PM10/22/12
to scala-i...@googlegroups.com
On Mon, Oct 22, 2012 at 8:33 AM, Josh Suereth <joshua....@gmail.com> wrote:
Sounds like a plan.  I don't think the goal of having minimal enumeration is bad, unless it locks us from improving it.  We have a lot of new vectors of attack we can use, from value classes to macros, and I think Enumeration is ripe for improvement.  If we wall understand the goals:

  • Lightweight, where lightweight = # of classes generated.
  • Typesafe, where one enumeration cannot be passed to another
  • Associates integer id w/ name
I think we could end up with a more "scala" enumeration class given those constraints.


Using an enumeration class like that sure would feel like premature optimization much of the time (vs. a sealed trait with case objects). If we already have both Java enums and case objects, now we're adding a third alternative which is called enum, but normally if you wanted a Java enum you should really use case objects ... kind of a pesky thing to explain to people.

Havoc

Paul Phillips

unread,
Oct 22, 2012, 6:38:49 PM10/22/12
to scala-i...@googlegroups.com
For the syntactic thing with case objects, which does annoy, I wonder if we could gift ourselves

  case object Monday, Tuesday, Wednesday, Thursday, Friday extend Weekday

It's not exactly out of left field given the existence of

  val Monday, Tuesday, Wednesday, Thursday, Friday = new Weekday


Jan Vanek

unread,
Oct 22, 2012, 9:56:02 PM10/22/12
to scala-i...@googlegroups.com
I think the proposed scheme could be changed (keeping all the extension points) so that a Java enum can be generated. We would still have the proprietary companion object extending the Enumeration/CustomEnumeration and the generated enum would implement the Enumerated trait. The Enumerated would have to remain a trait, so adding new fields would have to be done like:


object Status extends MyEnumeration("i18n/xy") {
  trait Enumerated extends super.Enumerated {
     val p1: String
     val p2: Int

     def eval(x: Double, y: Double): Double
     val flag = 1L << ordinal
  }

  RUNNING("out of bits", 0x01) { def eval(x: Double, y: Double) = x + y }
  FINISHED("now", 0xFF) { def eval(x: Double, y: Double) = x * y }
}

This means no extra constructors, the type-macro would infer the constructor needs p1 and p2, and no other are allowed. Also the Status class will be strictly generated.

This doesn't solve Som's requirement to have two/more sub-classes within one enum. And I don't know anymore whether generating java enum is the real goal.

Good night,
Jan

nafg

unread,
Oct 22, 2012, 10:16:30 PM10/22/12
to scala-i...@googlegroups.com

On Sunday, October 21, 2012 4:11:17 PM UTC-4, Rex Kerr wrote:
and allows (at compile-time) but breaks on repeated values (numeric constants allow repeats and do not break):

 Regarding the lack of compile-time safety, can that be solved by making def Value (or is it Value.apply?) a macro? Let it cache the values and generate a compile error if it finds a duplicate.

nafg

unread,
Oct 22, 2012, 10:24:20 PM10/22/12
to scala-i...@googlegroups.com
+1!

(Although it may need to be extends...)

Paul Phillips

unread,
Oct 22, 2012, 10:57:05 PM10/22/12
to scala-i...@googlegroups.com


On Mon, Oct 22, 2012 at 7:24 PM, nafg <nafto...@gmail.com> wrote:
(Although it may need to be extends...)

Oops, the subconscious grammar pedant overrode the intention.  I meant to type extends.

Paul Phillips

unread,
Oct 22, 2012, 10:57:25 PM10/22/12
to scala-i...@googlegroups.com
On Mon, Oct 22, 2012 at 7:57 PM, Paul Phillips <pa...@improving.org> wrote:
Oops, the subconscious grammar pedant overrode the intention.  I meant to type extends.

At least I managed not to write "case objects Monday, Tuesday..." 

Stefan Zeiger

unread,
Oct 23, 2012, 6:09:43 AM10/23/12
to scala-i...@googlegroups.com
I still don't follow.

  object Flags extends Enumeration {
    val Forward, Occupied, Updated, ArrayBacked, Extra = Value
  }
 
  println((Flags.Forward + Flags.Updated).toBitMask(0))

  def isForwardOccupied(l: Long) =
    (Flags.Forward + Flags.Occupied) subsetOf Flags.ValueSet.fromBitMask(Array(l))


  val ForwardFlag = 0x01
  val OccupiedFlag = 0x02
  val UpdatedFlag = 0x04
  val ArrayBackedFlag = 0x08
  val ExtraFlag = 0x10

  println(isForwardOccupied(ForwardFlag | UpdatedFlag))
  println(isForwardOccupied(ForwardFlag | OccupiedFlag | ExtraFlag))

  println(Flags.ValueSet.fromBitMask(Array(OccupiedFlag | UpdatedFlag)))

This prints:

  5
  false
  true
  Flags.ValueSet(Occupied, Updated)

Everything's nicely interoperable.

-sz

martin odersky

unread,
Oct 23, 2012, 6:25:45 AM10/23/12
to scala-i...@googlegroups.com
That's easy to do. But I'd only do it if at the same time we can ensure that we do not generate 2 classes for each object. Number of classes need to stay constant, no matter how many objects you define. That last one seems a bit trickier to achieve, because you still want enum constants to print their name with toString.

Cheers

 - Martin


 

Rex Kerr

unread,
Oct 23, 2012, 6:40:43 AM10/23/12
to scala-i...@googlegroups.com
I'm not saying that there are no facilities for dealing with bitmasks.  But if enums are for creating useful constants, this method requires considerable extra boilerplate if you actually need to get at the bitmask value.  If I have to
  Flags.ValueSet(Flags.Forward).toBitMask(0)
instead of
  Flags.Forward
every time I want to use a bitmask, I don't think I'm going to be particularly inclined to use Enumeration rather than val Forward = 0x1.  It's three times as much typing, I have four conceptual steps instead of one, and I create two temporary objects instead of doing a field access.  No thanks!

  --Rex

Simon Ochsenreither

unread,
Oct 23, 2012, 9:56:11 AM10/23/12
to scala-i...@googlegroups.com

And I don't know anymore whether generating java enum is the real goal.

I would say it is at least one of the goals. I have nothing against innovation, but I think it makes sense to start from an established implementation. Additionally, being incompatible with Java enums doesn't buy us anything but costs us a lot.

Imho the right way would be do start with a type macro to do the necessary magic of synthesizing the required superclass and methods, and let people add additional functionality with bog-standard traits.

Simon Ochsenreither

unread,
Dec 3, 2013, 10:12:24 PM12/3/13
to scala-i...@googlegroups.com
I'm happy to report that I managed to make a lot of progress (of course with tons of help by other people)!

Enum definition:

@enum
class Day {
  Monday
  Tuesday
  Wednesday
  Thursday
  Friday
  Saturday
  Sunday
}


Enum usage:

// Using enums in annotations works
public @interface DayAnnotation {
  // It's Friday, Friday
  Day bestDay() default Day.Friday;
}

// Some code:
@DayAnnotation(bestDay = Day.Monday) // annotation works
public class JavaDayConsumer {
    public static void main(String[] args) {
    System.out.println("Day.Monday");
    System.out.println(Day.Monday); // All the needed public static final fields exist
    System.out.println("Day.values()"); // The values() method exists and is correctly populated
    for (Day day : Day.values()) {
        System.out.println(day);
    }
    System.out.println("Day.valueOf(\"Monday\")"); // The valueOf(String) method works
    Day monday = Day.valueOf("Monday");
    System.out.println(monday);
    }
}


Output:

Day.Monday
Monday
Day.values()
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
Day.valueOf("Monday")
Monday


What's missing is support form Scala (shouldn't be too hard). Scala doesn't see the value Day, because I'm not generating a companion object and I guess scalac doesn't look for static methods inside of classes defined in Scala yet.

Rich Oliver

unread,
Dec 4, 2013, 3:39:39 PM12/4/13
to scala-i...@googlegroups.com
So currently we have:

Scala Enums: broken
Java Enums: Ugly, plus farming out syntax to Java is well just naff
Sealed Class and Objects: The Scala way but a lot of ceremony
Boolean: Horrendously unsafe. Booleans should only be used where there is some serious performance justification.

Paul's suggestion seems on the correct track, reduce the ceremony for Sealed Class and Objects. Depreciate Scala Enums and discourage the use of Java Enums and Boolean. That would be a grand simplificaton.


On Monday, October 22, 2012 11:38:51 PM UTC+1, Paul Phillips wrote:
For the syntactic thing with case objects, which does annoy, I wonder if we could gift ourselves

  case object Monday, Tuesday, Wednesday, Thursday, Friday extend Weekday
 
Other thoughts:

Why not just


object Monday, Tuesday, Wednesday, Thursday, Friday extend Weekday

Would Weekday be automatically sealed? Presumably toString methods for the objects would automatically be created. Would an iterator over the weekday objects be automatically created in the Weekday class?

Simon Ochsenreither

unread,
Jan 17, 2014, 7:15:55 PM1/17/14
to scala-i...@googlegroups.com
@enum
class DayConstructors(val abbr: String) {
  Monday("Mon")
  Tuesday("Tue")
  Wednesday("Wed")
  Thursday("Thu")
  Friday("Fri")
  Saturday("Sat")
  Sunday("Sun")
}

public class DayConstructorsConsumer {

    public static void main(String[] args) {
        System.out.println("Day.Monday");
        System.out.println(DayConstructors.Monday);
        System.out.println("Day.values()");
        for (DayConstructors day : DayConstructors.values()) {

            System.out.println(day);
        }
        System.out.println("Day.valueOf(\"Monday\")");
        DayConstructors monday = DayConstructors.valueOf("Monday");
        System.out.println(monday);
        System.out.println("Day.Monday.abbr()");
        System.out.println(monday.abbr());