Scala Enumeration and type projection

2010-12-30

Let‘s assume we have this simple Enumeration:

scala> object State extends Enumeration("created", "destroyed") {
|   type State = Value
|   val Created, Destroyed = Value
| }
defined module State

Scala has no language built-in Enumeration type like e.g. Java‘s enum type but provides an abstract class Enumeration in its standard library which can be used.
In this case we have an enumeration called State which has two values Created and Destroyed.

Now we define a container for enumerations which takes an enumeration value and provides a single method to return it:

scala> class Container[T <: Enumeration](value: T#Value) {
|   def get: T#Value = value
| }
defined class Container

Of course, this container can be used with our State enumeration:

scala> val cont = new Container(State.Created)
cont: Container[object State] = Container@dd23cf

scala> cont.get
res6: State#Value = created

Now we define a function which takes a state value from our State enumeration as its single parameter and returns another state:

scala> import State._
import State._

scala> def switch(state: State) =
|   if(state == Created) Destroyed else Created
switch: (state: State.State)State.Value

And here comes the problem:

scala> val cont = new Container(Created)
cont: Container[object State] = Container@c05c2

scala> switch(cont.get)
<console>:13: error: type mismatch;
found   : State#Value
required: State.State
switch(cont.get)

The compiler complains that it got a State#Value but requires something of type State.State. State.State__ is the same as State.Value since there‘s a type declaration in the State enumeration.

The first time I implemented this, I really haven‘t thought that this could be a problem. After some experimentation I came up with the following solution to make the compiler happy.

Solution

The idea is to use type projection in the switch function as well. The only problem is that you can‘t use type projection with objects.

This will fail:

scala> def switch(state: State#Value) =
|   if(state == State.Created) State.Destroyed else State.Created
<console>:6: error: not found: type State
def switch(state: State#Value) =

As mentioned before, type projection cannot be used with objects.

To get it working we have to define the enumeration as an abstract class with a companion object:

scala> sealed abstract class State extends Enumeration("created", "destroyed") {
|   type State = Value
|   val Created, Destroyed = Value
| }
defined class State

scala> object State extends State
defined module State

This allows us to change the function declaration to satisfy the compiler:

scala> def switch(state: State#Value) =
|   if(state == State.Created) State.Destroyed else State.Created
switch: (state: State#Value)State.Value

scala> switch(new Container(State.Created).get)
res2: State.Value = destroyed

Ok, the compiler is happy now and everything is working. But I‘m asking myself if this is the right solution. I really don‘t like the idea to declare all my enumerations in a sealed abstract class first.

If there‘s a better solution please tell me !!!


me

Marco Rico Gomez is a passionate software developer located in Germany who likes to share his thoughts and experiences about software development and technologies with others.


blog comments powered by Disqus