I’m writing a project in Scala. This project involves a set of features and a set of configurations, both extensible. By “extensible” I mean that I’m going to add new features in the hierarchy later, and they have to work with any configuration without recompilation. Here’s the feature hierarchy for the following illustration:
trait Feature {
def apply(board: Board)
}
class Foo extends Feature {
def apply(board: Board) {
println(board formatted "Foo: %s")
}
}
class Bar extends Feature {
def apply(board: Board) {
println(board formatted "Bar: %s")
}
}
A configuration basically just defines a lot of parameters for a Board, including initial feature count for each Feature. There are several possible strategies to create a configuration at run-time: predefined, random, with user-provided values, etc. In theory, I want to be able to write something like this (not a valid Scala code!):
abstract class Config(val param: Int) {
val ConfigParameter: Int
def featureCount[T <: Feature]: Int
}
object Config {
def makeBasic(param: Int) = new Config(param) {
val ConfigParameter = param
def featureCount[Foo] = 3
def featureCount[Bar] = 7
}
def makeRandom(param: Int) = new Config(param) { ... }
def makeWithUserValues(param: Int, ...) = new Config(param) { ... }
def makeByStandardISO1234567(param: Int) = new Config(param) { ... }
}
class Board(val config: Config) { ... }
Obviously, it doesn’t compile. My question is: what’s the best way to represent this extensible system in Scala? I can always include something like Map[Class, Int] in the Config, but it isn’t type-safe: a programmer can insert classes in such Map that aren’t Features. So, is there a way in the Scala type system to represent something like Map[Class[T <: Feature], Int] where different keys in the Map could be of different Feature subtypes? Alternatively, maybe there’s some way to move all this behavior to the Feature hierarchy?
Thank you.
You can use
ClassManifest(ClassTagin Scala 2.10) to improve your map solution:Each time you call
featureCountthe compiler will use the right classManifest for the type you passed between brackets. Theerasuremethod returns the coresponding class.Remark: Avoid abstract vals, it has annoying effects and can break binary compatibility.