Card from class [T] to T without casting

I want to map class tokens to instances in the lines of the following code:

trait Instances { def put[T](key: Class[T], value: T) def get[T](key: Class[T]): T } 

Is it possible to do this without having to allow castes in the get method?

Update:

How can this be done for a more general case with some Foo[T] instead of Class[T] ?

+7
scala
source share
3 answers

If you want to do this without casting (even within get ), you will need to write a heterogeneous map. For reasons that should be obvious, this is complicated. :-) The simplest way would probably be to use an HList like structure and build the find function. However, this is not trivial, since you need to define some way of checking type equality for two arbitrary types.

I tried to get a little confused with tuples and existential types. However, Scala does not provide a join mechanism (pattern matching does not work). In addition, subtyping ties it all together in nodes and basically eliminates any security that it could provide:

 val xs: List[(Class[A], A) forSome { type A }] = List( classOf[String] -> "foo", classOf[Int] -> 42) val search = classOf[String] val finalResult = xs collect { case (`search`, result) => result } headOption 

In this example, finalResult will be of type Any . This is actually correct, since subtyping means that we know nothing about A This is not why the compiler chooses this type, but it is the right choice. Take for example:

 val xs: List[(Class[A], A) forSome { type A }] = List(classOf[Boolean] -> 'bippy) 

This is absolutely legal! Subtyping means that A in this case will be selected as Any . This is hardly what we want, but this is what you get. Thus, in order to express this restriction without tracking all types individually (using the HMap ), Scala would have to be able to express the restriction that the type is a specific type, and nothing more. Unfortunately, Scala does not have this ability, so we mainly focus on the general restriction.

Refresh Actually, this is not legal. Just tried it and the compiler kicked it out. I think that it worked only because Class is invariant in its type parameter. So, if Foo is a certain type that is invariant, you should be safe from this case. It still does not solve the problem of unification, but at least it sounds. Unfortunately, type constructors are supposed to be in a magical superposition between co-, contra- and invariance, so if this is really an arbitrary type Foo form * => * , then you are still immersed in an existential front.

In short: this should be possible, but only if you fully encode Instances as an HMap . Personally, I would just throw get inside. Much easier!

+2
source share

You can try to get an object from your map as Any , and then use Class[T] to "mirror":

 trait Instances { private val map = collection.mutable.Map[Class[_], Any]() def put[T](key: Class[T], value: T) { map += (key -> value) } def get[T](key: Class[T]): T = key.cast(map(key)) } 
+9
source share

With the help of my friend, we defined the key card as a manifest instead of the class that gives the best api when called.

I did not receive your updated question about the "general case with some Foo [T] instead of the class [T]". But this should work for the cases you specify.

 object Instances {                                        
   private val map = collection.mutable.Map [Manifest [_], Any] ()
   def put [T: Manifest] (value: T) = map + = manifest [T] -> value
   def get [T: Manifest]: T = map (manifest [T]). asInstanceOf [T]

   def main (args: Array [String]) {
     put (1)
     put ("2")
     println (get [Int])
     println (get [String])
   }
 }
+5
source share

All Articles