tags:

views:

142

answers:

2

I have a problem with specifying types for enumeration values (instances of scala.Enumeration) in functions. This originally arises from my need to serialize enumeration objects in database, but I've extracted the problematic code in the following example:

object EnumerationTypes {

  class EnumerationProcessor[E <: Enumeration](enum: E, value: E#Value) {
    def process: E#Value = {
      value
    }
  }

  object Enum extends Enumeration {
    type EnumValue = Value

    val a = Value(1, "a")
    val b = Value(2, "b")
  }

  case class Obj(val flag: Enum.EnumValue)

  def main(args: Array[String]) {
    val processor = new EnumerationProcessor(Enum, Enum.a)
    val obj = Obj(processor.process)
  }
}

It leads to the following compilation error:

error: type mismatch;
 found   : EnumerationTypes.Enum#Value
 required: EnumerationTypes.Enum.EnumValue
    val obj = Obj(processor.process)

While this works ok:

object EnumerationTypesOk {

  class EnumerationProcessor[E <: Enumeration](enum: E, value: E#Value) {
    def process: E#Value = {
      value
    }
  }

  class Enum extends Enumeration {
    type EnumValue = Value

    val a = Value(1, "a")
    val b = Value(2, "b")
  }
  object Enum extends Enum

  case class Obj(val flag: Enum#EnumValue)

  def main(args: Array[String]) {
    val processor = new EnumerationProcessor(Enum, Enum.a)
    val obj = Obj(processor.process)
  }
}

But I don't want my code to be looks like this (first define class and then its singleton instance).

So the problem: how I can make value type be exactly the enum.EnumValue? While it seems impossible, because types cannot depend on concrete values, maybe there are some tricks to achieve desired effect with no additional boilerplate.

+1  A: 

Edit:

Looks like you just need to help the type inferencer a bit to get your first solution working:

val processor = new EnumerationProcessor[Enum.type](Enum, Enum.a)

Hopefully someone smarter than I am will come along and explain why.

Before the OP's clarifying comment:

object EnumerationTypes {

   class EnumerationProcessor[E <: Enumeration, V <: E#Value](enum: E, value: V) {
      def process: V = {
        value
      }
   } 

   object Enum extends Enumeration {
      type EnumValue = Value

      val a = Value(1, "a")
      val b = Value(2, "b")
   }

   case class Obj(val flag: Enum.EnumValue)

   def main(args: Array[String]) {
      val processor = new EnumerationProcessor(Enum, Enum.a)
      val obj = Obj(processor.process)
   }
}
Aaron Novstrup
The problem with this solutions is `V` is not compatible with `E.Value` — and if I want to do inside processor something like `enum.values.find { _ == value }` it will not typecheck.
andreypopp
I updated my answer -- it should cover this use case now.
Aaron Novstrup
+1  A: 

Another potential solution is to define a mixin that allows you to create a 'processor' for a particular Enumeration instance:

object EnumerationTypes {
   trait EnumerationProcessor { self: Enumeration =>
      def processor(value: self.Value): () => self.Value = () => { 
         value 
      }
   }

   object Enum extends Enumeration with EnumerationProcessor {
      type EnumValue = Value

      val a = Value(1, "a")
      val b = Value(2, "b")
   }

   case class Obj(val flag: Enum.EnumValue)

   def main(args: Array[String]) {
      val processor = Enum.processor(Enum.a)
      val obj = Obj(processor())
   }
}
Aaron Novstrup