scalagenericstypesimplicit-conversionscala-generics

Scala - Acessing the type of a parameterized class


Imagine I have a Box that can hold animals of some type.
This Box can be given raw data which should be transformed/serialized into an animal of the same type of those already in the Box.
In other words, if a Box of dogs is given some "data", I want to validate that the "data" is another dog.

trait Animal
trait Dog extends Animal
trait Cat extends Animal   

 class Box[T<:Animal](elems: List[T]) {

      def receiveNewAnimal(data: String): T = validate[T](data)(<implicit val>)
    }

Normally, when I want to validate "rawData" against a particular Animal, I would just do (example for dogs):

val getMyDog(data: String): Dog = validate[Dog](data)

However, I do not know which type of Animal a Box[T] holds.
If I let it be as is:

def receiveNewAnimal(data: String): T = validate[T](data)(<implicit val>)

I get a compilation error saying that I do not have an implicit for type T (even tough I have all the implicits possible for the sub-traits of Animal).
It appears I am not being able to tell the compiler, that I want to validate the data against the type of Animal the current Box contains.

  1. Why does this happen?
  2. Can it be solved and how?

Solution

  • You have to transport the implicit value from the call-site at which the Box constructor is invoked to the validate method.


    Assuming that there is something like the following typeclass:

    trait Validatable[T]
    

    and that validate requires T to be Validatable:

    def validate[T : Validatable](serializedData: String): T = ???
    

    you can do this:

    trait Animal
    trait Dog extends Animal
    trait Cat extends Animal   
    
    class Box[T <: Animal : Validatable](elems: List[T]) {
      def receiveNewAnimal(data: String): T = validate[T](data)
    }
    

    Alternatively, if your validate is declared with a second parameter list, you can do the same with the Box:

    def validate2[T](serializedData: String)(implicit v: Validatable[T]): T = ???
    
    class Box2[T <: Animal](elems: List[T])(implicit v: Validatable[T]) {
      def receiveNewAnimal(data: String): T = validate[T](data)(v)
    }
    

    What Validatable actually is isn't that important in this case, could be some ClassTag, could be some macro-generated token that provides some deserialization strategy.