Existential types and pattern matching in Scala
I'm trying to do something roughly like the following:
trait MyData
trait MyId
trait MyDataType[T <: MyData] {
type MyIdType <: MyId
// There can be converters here to bring back
// lost type information.
}
trait Writer[T <: MyData] {
def save(data: Map[T#MyIdType, T])
}
val writers: Map[MyDataType[_ <: MyData], Writer[_ <: MyData]]
val data: Map[MyDataType[_ <: MyData], Map[MyId, MyData]]
// mapping from id -> data grouped by the type of data.
// We've now lost the type safety since this is just a big bag.
data.foreach { case (type, map) =>
writer.get(type).save(map)
// DOES NOT COMPILE SINCE IT CAN'T GUARANTEE WRITER AND
// MAP ARE OF SAME TYPE
}
I'd like to change this to something like
data.foreach {
case (type: MyDataType[T], map: Map[T#MyIdType, T]) forSome {
type T <: MyData } =>
// do save logic
// COMPILER COMPLAINS - not found: type T
}
but it doesn't look like I can use existential types in 开发者_Python百科case statements. Note that I don't care about missing type safety at this point because my data is already grouped by type, so I just want a way to force the compiler to accept a type I through at it. Any suggestions? I also tried to parameterize a case statement but that was a no go:
data.foreach {
case [T <: MyData](type: MyDataType[T], map: Map[T#MyIdType, T]) =>
// do save logic
// COMPILER COMPLAINS -
// illegal start of simple pattern for the parameterization
}
Any ideas of how to do what I want?
I am tempted to say, that even if there is a solution to your question, there's something which does not sound right in your design. On one hand, you use complex types which are nice for type safety and inference at compile time. On the other hand you use a map to store runtime information. So either, you simplify your traits and forget about type-saftey, or you forget about storing types in maps at runtime.
For example, if you want to associate a writer to a particular MyData subclass, you could use type classes, which are flexible and resolved at compile-time.
Does this is what you want?
trait MyData
trait MyId
trait Writer[T <: MyData] {
def save(data: T)
}
var writers: Map[Manifest[_ <: MyData], Writer[MyData]] = Map.empty
var data: Map[MyId, (MyData, Manifest[_ <: MyData])] = Map.empty
data.foreach {
case (id, (d, m)) =>
writers.get(m).map(_.save(d)) // [1]
}
def addData[T <: MyData](id: MyId, d: T)(implicit m: Manifest[T]) = {
data += ((id, (d, m)))
}
// you don't need to give the m parameter, it is given by the compiler
addData(new MyId {}, new MyData {})
- you surely want a better search logic here
精彩评论