Scala Enumeration

136 views
Skip to first unread message

G J

unread,
Feb 12, 2012, 9:17:14 PM2/12/12
to scala-language
Can someone explain why IDLE is always printed as 'x'?

object State extends Enumeration {
val IDLE, STARTED = Value
var x = IDLE
}

scala> State.x
res16: State.Value = x // okay...


scala> State.x = State.STARTED
State.x: State.Value = STARTED

scala> State.x
res17: State.Value = STARTED // expected.


scala> State.x = State.IDLE
State.x: State.Value = x // huh?

scala> State.x
res18: State.Value = x // what?

Thanks.

G J

unread,
Feb 12, 2012, 9:31:00 PM2/12/12
to scala-language
To add;

State.withName("IDLE")

generates an exception, whereas State.withName("STARTED") doesn't.

Thanks.

Stefan Zeiger

unread,
Feb 13, 2012, 4:42:57 AM2/13/12
to scala-l...@googlegroups.com
On 2012-02-13 3:17, G J wrote:
> Can someone explain why IDLE is always printed as 'x'?
>
> object State extends Enumeration {
> val IDLE, STARTED = Value
> var x = IDLE
> }

Enumeration uses some reflection hack to discover the names of the
values (if you don't specify them explicitly). There's a field named "x"
in your enumeration object, so the value is named "x". Apparently the
last field wins.

-sz

martin odersky

unread,
Feb 13, 2012, 5:55:28 AM2/13/12
to scala-l...@googlegroups.com

Yes, that's a bug. Please create a ticket!

Thanks

-- Martin

Jan Vanek

unread,
Feb 13, 2012, 6:02:38 AM2/13/12
to scala-l...@googlegroups.com
A possible fix to Enumeration's method populateNameMap()

  private def populateNameMap() {
    val fields = getClass.getDeclaredFields
    def isValDef(m: JMethod) = fields exists (fd => fd.getName == m.getName && fd.getType == m.getReturnType)

    // The list of possible Value methods: 0-args which return a conforming type
    val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty &&
                                                   classOf[Value].isAssignableFrom(m.getReturnType) &&
                                                   m.getDeclaringClass != classOf[Enumeration] &&
                                                   isValDef(m))
    val setters = getClass.getMethods filter (m => m.getParameterTypes.length == 1 &&
                                                   classOf[Value].isAssignableFrom(m.getParameterTypes.apply(0)) &&
                                                   classOf[Unit] == m.getReturnType &&
                                                   m.getDeclaringClass != classOf[Enumeration] &&
                                                   m.getName.endsWith("_$eq"))
    methods foreach { m =>
      val name = m.getName
      if (!setters.exists(m => m.getName == name + "_$eq")) {
        // invoke method to obtain actual `Value` instance
        val value = m.invoke(this).asInstanceOf[Value]
        // verify that outer points to the correct Enumeration: ticket #3616.
        if (value.outerEnum eq thisenum) {
          val id = Int.unbox(classOf[Val] getMethod "id" invoke value)
          nmap += ((id, name))
        }
      }
    }
  }

It would still not solve following:

object State extends Enumeration {
  val IS_FIRST = Value
  val WHICH_ONE = IS_FIRST
}

With regards,
Jan

Trond Olsen

unread,
Feb 13, 2012, 11:14:10 AM2/13/12
to scala-l...@googlegroups.com
I reget contributing life-support to Enumeration. You should too! :) Anyway, anything useful in the new reflection api that could be used in a rewrite?

Stefan Zeiger

unread,
Feb 13, 2012, 11:44:15 AM2/13/12
to scala-l...@googlegroups.com

That's a great explanation I came up with, don't you think? There's just
one flaw: It's wrong! I can't reproduce this on either 2.9.1 or some
nightly build from 2 weeks ago (long after my Enumeration changes were
merged into master). In both versions, the enum value is named IDLE, not x.

So, on which version is this supposed to happen? There are a couple of
very similar bugs in JIRA
(https://issues.scala-lang.org/browse/SI-5211), some of them already closed.

-sz

G J

unread,
Feb 14, 2012, 5:33:03 AM2/14/12
to scala-language
This happens in Scala version 2.9.1.final (OpenJDK 64-Bit Server VM,
Java 1.7.0_b147-icedtea).

Thanks.

Stefan Zeiger

unread,
Feb 14, 2012, 5:55:26 AM2/14/12
to scala-l...@googlegroups.com
On 2012-02-14 11:33, G J wrote:
> This happens in Scala version 2.9.1.final (OpenJDK 64-Bit Server VM,
> Java 1.7.0_b147-icedtea).

Except for OpenJDK, that's what I am using, too:

Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM,
Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.

scala> object State extends Enumeration {


| val IDLE, STARTED = Value
| var x = IDLE
| }

defined module State

scala> State.x
res0: State.Value = IDLE

scala> State.IDLE
res1: State.Value = IDLE

-sz

Marius Danciu

unread,
Feb 14, 2012, 6:13:57 AM2/14/12
to scala-l...@googlegroups.com
Wouldn't it be possible that an built-in compiler plugin to figure out the enumeration content instead of using reflection ?

Thanks,
Marius

G J

unread,
Feb 14, 2012, 6:46:16 AM2/14/12
to scala-language
Absolutely amazing! So much for Java portability.

As clear as day light, it fails under OpenJDK [or in my version].
Anyone have a deeper understanding of the differences? Which one is
right....?

Thanks.

Jason Zaugg

unread,
Feb 14, 2012, 6:56:04 AM2/14/12
to scala-l...@googlegroups.com
On Tue, Feb 14, 2012 at 12:46 PM, G J <oda...@gmail.com> wrote:
>
> Absolutely amazing! So much for Java portability.
>
> As clear as day light, it fails under OpenJDK [or in my version].
> Anyone have a deeper understanding of the differences?  Which one is
> right....?

"The elements in the array returned are not sorted and are not in any
particular order"

http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Class.html#getMethods()

-jason

Simon Ochsenreither

unread,
Feb 14, 2012, 6:57:48 AM2/14/12
to scala-l...@googlegroups.com
I would propose a compiler plugin to figure out if Enumerations is broken. For simplicity, it could be implemented by ignoring the input arguments and always returning true.

Jan Vanek

unread,
Feb 14, 2012, 7:40:44 AM2/14/12
to scala-l...@googlegroups.com
object State extends Enumeration {
  val IDLE = Value
  val /*or var*/ N: Value = null
}
def main(args: Array[String]) {
println(State.IDLE)
}

produces:
Exception in thread "main" java.lang.NullPointerException
at scala.Enumeration$$anonfun$scala$Enumeration$$populateNameMap$1.apply(Enumeration.scala:173)
at scala.Enumeration$$anonfun$scala$Enumeration$$populateNameMap$1.apply(Enumeration.scala:168)
at scala.collection.IndexedSeqOptimized$class.foreach(IndexedSeqOptimized.scala:34)
at scala.collection.mutable.ArrayOps.foreach(ArrayOps.scala:38)
at scala.Enumeration.scala$Enumeration$$populateNameMap(Enumeration.scala:168)

Very minor and can easily be fixed:
old:    if (value.outerEnum eq thisenum) {
new:    if (value != null && (value.outerEnum eq thisenum)) {

With regards,
Jan

Dave

unread,
Feb 14, 2012, 8:06:42 AM2/14/12
to scala-language
I can confirm:


C:\Users\Dave>scala
Welcome to Scala version 2.10.0-M1 (Java HotSpot(TM) Client VM, Java
1.6.0_30).
Type in expressions to have them evaluated.
Type :help for more information.

scala> object State extends Enumeration {
| var IDLE, STARTED = Value
| var x = IDLE
| }
defined module State

scala> State.x
res0: State.Value = x

scala> State.x = State.STARTED
State.x: State.Value = STARTED

scala> State.x
res1: State.Value = STARTED

scala> State.x = State.IDLE
State.x: State.Value = x

scala> State.x
res2: State.Value = x

Stefan Zeiger

unread,
Feb 14, 2012, 8:07:05 AM2/14/12
to scala-l...@googlegroups.com
On 2012-02-14 12:56, Jason Zaugg wrote:
> On Tue, Feb 14, 2012 at 12:46 PM, G J<oda...@gmail.com> wrote:
>> Absolutely amazing! So much for Java portability.
>>
>> As clear as day light, it fails under OpenJDK [or in my version].
>> Anyone have a deeper understanding of the differences? Which one is
>> right....?
> "The elements in the array returned are not sorted and are not in any
> particular order"

I've opened https://issues.scala-lang.org/browse/SI-5462. Scala
reflection might give us the tools to improve this situation. OTOH,
macros could allow a much better implementation altogether.

-sz

Jan Vanek

unread,
Feb 14, 2012, 8:31:38 AM2/14/12
to scala-l...@googlegroups.com

On Tue, Feb 14, 2012 at 12:57 PM, Simon Ochsenreither <simon.och...@googlemail.com> wrote:
I would propose a compiler plugin to figure out if Enumerations is broken. For simplicity, it could be implemented by ignoring the input arguments and always returning true.

:-)

class StateClass extends Enumeration {
  val IDLE = Value
}
val State = new StateClass
def main(args: Array[String]) {
   val bais = new ByteArrayOutputStream
   val ois = new ObjectOutputStream(bais)
   ois.writeObject(State.IDLE)
   ois.flush
   val baos = new ByteArrayInputStream(bais.toByteArray())
   val oos = new ObjectInputStream(baos)
   val state = oos.readObject().asInstanceOf[State.Value]
   println(state)
}

Serializable, huh?

Exception in thread "main" java.io.IOException: unexpected exception type
at java.io.ObjectStreamClass.throwMiscException(Unknown Source)
at java.io.ObjectStreamClass.invokeReadResolve(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at scalaf.T$.main(T.scala:21)
at scalaf.T.main(T.scala)
Caused by: java.lang.NoSuchFieldException: MODULE$
at java.lang.Class.getField(Unknown Source)
at scalaf.Enumeration.readResolve(Enumeration.scala:64)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
... 10 more

Jan Vanek

unread,
Feb 14, 2012, 9:16:34 AM2/14/12
to scala-l...@googlegroups.com
It is related to: https://issues.scala-lang.org/browse/SI-2214 

Should have looked before... I think the enum should provide writeReplace method which would - in case the enum itself really is an object (determinable at runtime) - not serialize all its members, but only the class name. During deserialization it should read-resolve to the companion object. In the case the enum instance is not an object instance it would probably be best to serialize and deserialize it fully (into new instance, as usual objects).

Jan Vanek

unread,
Feb 15, 2012, 6:11:47 AM2/15/12
to scala-l...@googlegroups.com
The posts about enumeration are getting annoying, sorry. I've created a gist which tries to fix/improve the serialization of the enums. Whenever someone redesigns the class hopefully to provide user customizable Value classes he could still use the method. When serializing a "static object" (an object which is not nested in a class) enum only its class (boxed in private ObjectRepl class) is serialized, when serializing a value of such enum only its id (wrapped in private ValRepl class) is serialized. Other enums and their values are serialized fully.


Reply all
Reply to author
Forward
0 new messages