Reputation: 85
I'm trying to add a function apply
to my Enumeration
s. I want to map enumerated values to functions and later on use it like this:
Action(DO_THIS, "Arg string")
which should print it's value. I have multiple enum classes and I'm trying to implement this functionality once via a trait mixin. So far I've failed to make compiler happy. I have this code that does not compile:
trait EnumFunc [EnumType <: Enumeration] {
protected def funcMappings: Map[EnumType#Value, Function1[String, Unit]]
def apply(enum: EnumType#Value, arg: String): Unit =
funcMappings.getOrElse(enum, (arg: String) => ())(arg)
}
trait Action extends Enumeration with EnumFunc[Action] {
type Action = Value
val DO_THIS, DO_THAT = Value
override val funcMappings: Map[Action, Function1[String, Unit]] =
Map(DO_THIS -> ((arg: String) => println(arg)))
}
object Action extends Action
The compiler produces this error:
error: overriding method funcMappings in trait EnumFunc of type => Map[Action#Value,String => Unit];
value funcMappings has incompatible type
override val funcMappings: Map[Action, Function1[String, Unit]] =
I can't figure out exactly what the problem is. Could someone please explain if it's possible to achieve this at all and why this does not compile?
Upvotes: 2
Views: 250
Reputation: 13667
Value
inside of Action
and EnumType#Value
are not quite the same. Value
inside of Action
is a path-dependent type, it can only refer to instances of Values
that have this
as their parent, i.e. Value
is a synonym for this.Value
. EnumType#Value
is a type projection, it can refer to any value from an instance of EnumType
, and doesn't care what it's parent is. So the declaration of funcMappings
and apply
in Action
are in fact being given stricter parameter types, which is not type-safe.
However, the type parameter on EnumType
isn't actually needed, if you extend Enumeration
you can use Value
directly:
trait EnumFunc extends Enumeration {
protected def funcMappings: Map[Value, Function1[String, Unit]]
def apply(enum: Value, arg: String): Unit =
funcMappings.getOrElse(enum, (arg: String) => ())(arg)
}
and then it will work. However, this is just one of the many examples of how Scala's Enumeration
is an abomination. Once you know you want to add methods to the individual elements, it's much better to use a hierarchy of case classes/objects:
abstract class Doers {
def apply(s: String): Unit
}
case object DoThis extends Doers {
def apply(s: String): Unit = println(s)
}
case object DoThat extends Doers {
def apply(s: String): Unit = ???
}
Upvotes: 1