Let X be a type constructor with type parameters A1, A2, ..., An. For example Option[A] and Function1[A1, A2].
Let X[T1, T2, ..., Tn] be the type resulting of applying the type constructor X to the concrete type argument T1, T2, ... Tn. For example Option[Int] and Function1[Long, List[String]].
Let Y be a direct subclass of X that leaves some of the type parameters of X unfixed, does not add new free type parameter, and its type parameters are B1, B2, ..., Bm with m <= n. For example Some[B] and PartialFunction[B1, B2].
I need to implement a function that finds out the concrete types R1, R2, ..., Rm to be assigned to the type parameters B1, B2, ..., Bm of the type constructor Y such that Y[R1, R2, ..., Rm] <:< X[T1, T2, ..., Tn] with all the variances removed (all traits and classes considered non variant).
In the case of Option and Some it is clear that R1 = T1 for Some[R1] <:< Option[T1] with variance removed be true.
Also, in the case of Function1 and PartialFunction is clear that R1 = T1 and R2 = T2 for PartialFunction[R1, R2] <:< Function[T1, T2] with variance removed be true.
But for the general case is somewhat more complicated.
Given that both, the compiler and the <:< operator of the reflect library, have to solve this problem in order to check assignability; I assume that a function of the reflection API already solves my problem. But I don't find it.
Y suppose it is def asSeenFrom(pre: Type, clazz: Symbol): Type, but I tried it with no avail. Surely, I am doing something wrong.
This is an usage example of the function that answers this question:
import scala.reflect.runtime.universe._
val optionOfInt: Type = typeOf[Option[Int]]
val someTypeConstructor: ClassSymbol = typeOf[Some[_]].typeSymbol.asClass
val someOfInt: Type = instantiateSubclassTypeConstructor(optionOfInt, someTypeConstructor)
print(someOfInt.typeArgs) // outputs "List(Int)"
Where instantiateSubclassTypeConstructor is the function which answers this question.
/** @param baseInstantiation a class constructor instantiation. For example: {{{typeOf[Option[Int]]}}}
* @param directSubclassSymbol the [[ClassSymbol] of the class constructor we want to instantiate such that it is assignable to `baseInstantiantion`. For example: {{{typeOf[Some[_]].typeSymbol.asClass}}}
* @return a type instantiation of the class constructor referenced by the `directSubclassSymbol` such that it is assignable to `baseInstantiantion`. For example: {{{typeOf[Some[Int]]}}}
*/
def instantiateSubclassTypeConstructor(baseInstantiation: Type, directSubclassSymbol: ClassSymbol): Type = ???
scala version: 2.13.3
it is clear that R1 = T1 for Some[R1] <:< Option[T1]is that right though? I think thatSome[R1] <:< Option[T1]iffR1 <:< T1. And are you aware of type erasure? I believe, what you're trying to solve can't be solved for the general case. Only for some very specific cases where type definitions preserved at compile time. - SimY4def asSeenFrom(pre: Type, clazz: Symbol): Typedefined inscala.reflect.api.Types. Apparently it solves this problem. But I am unable to understand how to use it. The example in the doc is obsolete because it usesThisTypewhich was removed from the API. - Readreninternal.thisType(C)instead ofThisType(C). - Dmytro Mitin