Domanda

I caught myself watching a bit of the Scalawags#2 recording, and then there came this part about type erasure and Dick Wall pointing out that reflection will eventually bite you in the feet.

So I was thinking about something that I'm doing quite frequently (and I saw it the implementation of Scala Collections as well). Let's say I have a system with a serializer taking the system as type parameter:

trait Sys[S <: Sys[S]] { type Tx }
trait FooSys extends Sys[FooSys]

trait Serializer[S <: Sys[S], A] {
  def read(implicit tx: S#Tx): A
}

Now there are many types A for which serializers can be constructed without value parameters, so essentially the system type parameter is "hollow". And since serializers are heavily invoked in my example, I'm saving instantiation:

object Test {
  def serializer[S <: Sys[S]] : Serializer[S, Test[S]] =
    anySer.asInstanceOf[Ser[S]]

  private val anySer = new Ser[FooSys]

  private final class Ser[S <: Sys[S]] extends Serializer[S, Test[S]] {
    def read(implicit tx: S#Tx) = new Test[S] {} // (shortened for the example)
  }
}
trait Test[S <: Sys[S]]

I know this is correct, but of course, asInstanceOf has a bad smell. Are there any suggestions to this approach? Let me add two things

  • moving the type parameter from the constructor of trait Serializer to the read method is not an option (there are specific serializers which require value arguments parametrised in S)
  • adding variance to Serializer's type constructor parameter is not an option
È stato utile?

Soluzione

Introduction:

I am a little confused by your example and I might have misunderstood your question, I have a feeling there is a certain type recursion between S and Tx that I am not getting from your question(because if not, S#Tx could be anything and I don't understand the problem with the anySer)

Tentative Answer:

At compile time, for any instance of Ser[T] there will be a well-defined type parameter T, since you want to save it on instantiation, you will have a single anySer Ser[T] for a given specific type A

What you are saying in some way is that a Ser[A] will work as Ser[S] for any S. This can be explained in two ways, according to the relationship between type A and S.

  1. If this conversion is possible for every A<:<S then your serializer is COVARIANT and you can initialize your anySer as a Ser[Nothing] . Since Nothing is subclass of every class in Scala, your anySer will always work as a Ser[Whatever]

  2. If this conversion is possible for every S<:<A then your serializer is CONTRAVARIANT and you can initialize your anySer as a Ser[Any] . Since Any is subclass of every class in Scala, your anySer will always work as a Ser[Whatever]

  3. If it's neither the one of the previous case, then it means that:

    def serializer[S <: Sys[S]] : Serializer[S, Test[S]] =
    anySer.asInstanceOf[Ser[S]]
    

    Could produce an horrible failure at runtime, because there will some S for which the Serializer won't work. If there are no such S for which this could happen, then your class falls in either 1 or

Comment post-edit

If your types are really invariant, the conversion through a cast breaks the invariance relation. You are basically forcing the type system to perform an un-natural conversion because you know that nothing wrong will happen, on the basis of your own knowledge of the code you have written. If this is the case then casting is the right way to go: you are forcing a different type from the one the compiler can check formally and you are making this explicit. I would even put a big comment saying why you know that operation is legal and the compiler can't guess and eventually attach a beautiful unit test to verify that the "in-formal" relation always holds.

In general, I believe this practice should be used with extreme care. One of the benefits of strongly typed languages is that the compiler performs formal type checking that helps you catch early errors. If you intentionally break it, you give away this big benefit.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top