- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Scala Enumeration
Mon, 2012-02-13, 03:17
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.
Mon, 2012-02-13, 10:51
#2
Re: Scala Enumeration
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
Mon, 2012-02-13, 12:01
#3
Re: Scala Enumeration
On Mon, Feb 13, 2012 at 10:42 AM, Stefan Zeiger wrote:
> 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.
Yes, that's a bug. Please create a ticket!
Thanks
Mon, 2012-02-13, 12:11
#4
Re: Re: Scala Enumeration
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
On Mon, Feb 13, 2012 at 3:31 AM, G J <odaym2@gmail.com> wrote:
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
On Mon, Feb 13, 2012 at 3:31 AM, G J <odaym2@gmail.com> wrote:
To add;
State.withName("IDLE")
generates an exception, whereas State.withName("STARTED") doesn't.
Thanks.
On Feb 12, 9:17 pm, G J <oda...@gmail.com> wrote:
> 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.
Mon, 2012-02-13, 17:21
#5
Re: Re: Scala Enumeration
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?
On 13 February 2012 12:02, Jan Vanek <j3vanek@googlemail.com> wrote:
On 13 February 2012 12:02, Jan Vanek <j3vanek@googlemail.com> wrote:
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
On Mon, Feb 13, 2012 at 3:31 AM, G J <odaym2@gmail.com> wrote:
To add;
State.withName("IDLE")
generates an exception, whereas State.withName("STARTED") doesn't.
Thanks.
On Feb 12, 9:17 pm, G J <oda...@gmail.com> wrote:
> 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.
Mon, 2012-02-13, 17:51
#6
Re: Scala Enumeration
On 2012-02-13 10:42, Stefan Zeiger wrote:
> 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.
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
Tue, 2012-02-14, 11:41
#7
Re: Scala Enumeration
This happens in Scala version 2.9.1.final (OpenJDK 64-Bit Server VM,
Java 1.7.0_b147-icedtea).
Thanks.
On Feb 13, 11:44 am, Stefan Zeiger wrote:
> On 2012-02-13 10:42, Stefan Zeiger wrote:
>
> > 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.
>
> 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
Tue, 2012-02-14, 12:11
#8
Re: Re: Scala Enumeration
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
Tue, 2012-02-14, 12:41
#9
Re: Re: Scala Enumeration
Wouldn't it be possible that an built-in compiler plugin to figure out the enumeration content instead of using reflection ?
Thanks,
Marius
On Mon, Feb 13, 2012 at 1:02 PM, Jan Vanek <j3vanek@googlemail.com> wrote:
Thanks,
Marius
On Mon, Feb 13, 2012 at 1:02 PM, Jan Vanek <j3vanek@googlemail.com> wrote:
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
On Mon, Feb 13, 2012 at 3:31 AM, G J <odaym2@gmail.com> wrote:
To add;
State.withName("IDLE")
generates an exception, whereas State.withName("STARTED") doesn't.
Thanks.
On Feb 12, 9:17 pm, G J <oda...@gmail.com> wrote:
> 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.
Tue, 2012-02-14, 13:01
#10
Re: Scala Enumeration
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.
On Feb 14, 5:55 am, Stefan Zeiger wrote:
> 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
Tue, 2012-02-14, 13:01
#11
Re: Re: Scala Enumeration
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.
Tue, 2012-02-14, 13:21
#12
Re: Re: Scala Enumeration
On Tue, Feb 14, 2012 at 12:46 PM, G J 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
Tue, 2012-02-14, 13:51
#13
Re: Re: Scala Enumeration
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
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
Tue, 2012-02-14, 14:11
#14
Re: Scala Enumeration
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
On 13 feb, 03: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
>
> }
>
> 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.
Tue, 2012-02-14, 14:21
#15
Re: Re: Scala Enumeration
On 2012-02-14 12:56, Jason Zaugg wrote:
> On Tue, Feb 14, 2012 at 12:46 PM, G J 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
Tue, 2012-02-14, 14:51
#16
Re: Re: Scala Enumeration
On Tue, Feb 14, 2012 at 12:57 PM, Simon Ochsenreither <simon.ochsenreither@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
Tue, 2012-02-14, 15:31
#17
Re: Re: Scala Enumeration
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).
On Tue, Feb 14, 2012 at 2:31 PM, Jan Vanek <j3vanek@googlemail.com> wrote:
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).
On Tue, Feb 14, 2012 at 2:31 PM, Jan Vanek <j3vanek@googlemail.com> wrote:
On Tue, Feb 14, 2012 at 12:57 PM, Simon Ochsenreither <simon.ochsenreither@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
To add;
State.withName("IDLE")
generates an exception, whereas State.withName("STARTED") doesn't.
Thanks.
On Feb 12, 9:17 pm, G J wrote:
> 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.