Type.childType(clazz)

74 views
Skip to first unread message

Eugene Burmako

unread,
Mar 26, 2013, 10:32:19 PM3/26/13
to scala-internals
There's `def baseType(clazz: Symbol): Type` in Types.scala, which
returns a supertype based on a superclass symbol. E.g. for `class
C[T]; class D[T]`, typeOf[D[Int]].baseType(symbolOf[C]) will return
typeOf[C[Int]].

Is there a way to climb the inheritance tree in the inverse direction?
E.g. in the example above go from typeOf[C[Int]] and symbolOf[D] to
typeOf[D[Int]].

Jason Zaugg

unread,
Mar 27, 2013, 2:43:10 AM3/27/13
to scala-i...@googlegroups.com
I'm a bit sketchy on the details, and might have missed a higher level API do do the same, but I think you need to run type inference to go in that direction:


-jason

Paul Phillips

unread,
Mar 27, 2013, 2:43:50 AM3/27/13
to scala-i...@googlegroups.com

On Tue, Mar 26, 2013 at 7:32 PM, Eugene Burmako <eugene....@epfl.ch> wrote:
Is there a way to climb the inheritance tree in the inverse direction?
E.g. in the example above go from typeOf[C[Int]] and symbolOf[D] to
typeOf[D[Int]].

I went to some lengths.


/** On pattern matcher checkability:
 *
 *  Consider a pattern match of this form: (x: X) match { case _: P => }
 *
 *  There are four possibilities to consider:
 *     [P1] X will always conform to P
 *     [P2] x will never conform to P
 *     [P3] X <: P if some runtime test is true
 *     [P4] X cannot be checked against P
 *
 *  The first two cases correspond to those when there is enough
 *  static information to say X <: P or that (x ∈ X) ⇒ (x ∉ P).
 *  The fourth case includes unknown abstract types or structural
 *  refinements appearing within a pattern.
 *
 *  The third case is the interesting one.  We designate another type, XR,
 *  which is essentially the intersection of X and |P|, where |P| is
 *  the erasure of P.  If XR <: P, then no warning is emitted.
 *
 *  Examples of how this info is put to use:
 *  sealed trait A[T] ; class B[T] extends A[T]
 *    def f(x: B[Int]) = x match { case _: A[Int] if true => }
 *    def g(x: A[Int]) = x match { case _: B[Int] => }
 *
 *  `f` requires no warning because X=B[Int], P=A[Int], and B[Int] <:< A[Int].
 *  `g` requires no warning because X=A[Int], P=B[Int], XR=B[Int], and B[Int] <:< B[Int].
 *      XR=B[Int] because a value of type A[Int] which is tested to be a B can
 *      only be a B[Int], due to the definition of B (B[T] extends A[T].)
 *
 *  This is something like asSeenFrom, only rather than asking what a type looks
 *  like from the point of view of one of its base classes, we ask what it looks
 *  like from the point of view of one of its subclasses.
 */

Paul Phillips

unread,
Mar 27, 2013, 2:46:27 AM3/27/13
to scala-i...@googlegroups.com

On Tue, Mar 26, 2013 at 11:43 PM, Jason Zaugg <jza...@gmail.com> wrote:
I'm a bit sketchy on the details, and might have missed a higher level API do do the same, but I think you need to run type inference to go in that direction:


That makes for an example:

scala> analyzer.propagateKnownTypes(typeOf[C[Int]], d)
res0: $r.intp.global.analyzer.global.Type = D[Int]

Jason Zaugg

unread,
Mar 27, 2013, 2:51:53 AM3/27/13
to scala-i...@googlegroups.com
Yay for high-leveldom.

Would `to.instantiateTypeParams(tparams, resArgs)` be more correct that `appliedType(to, resArgs: _*)`? It seems to handle some special cases for annotated/existential/higher-kinded types.

-jason

Paul Phillips

unread,
Mar 27, 2013, 3:06:37 AM3/27/13
to scala-i...@googlegroups.com

On Tue, Mar 26, 2013 at 11:51 PM, Jason Zaugg <jza...@gmail.com> wrote:
Would `to.instantiateTypeParams(tparams, resArgs)` be more correct that `appliedType(to, resArgs: _*)`? It seems to handle some special cases for annotated/existential/higher-kinded types.

Probably. Any idea whether there's some good reason this distinction should exist? Is it handling cases I wouldn't want handled sometimes?

I notice that appliedType routes to instantiateTypeParams if the type constructor is a polytype, which means if the symbol-application appliedType was

  appliedType(tyconSym.typeConstructor.normalize, args)

instead of

  appliedType(tyconSym.typeConstructor, args)

Then instantiateTypeParams would be called. I assume a bunch of unrelated pain would arrive as well.

Jason Zaugg

unread,
Mar 27, 2013, 3:55:02 AM3/27/13
to scala-i...@googlegroups.com
On Wed, Mar 27, 2013 at 8:06 AM, Paul Phillips <pa...@improving.org> wrote:

On Tue, Mar 26, 2013 at 11:51 PM, Jason Zaugg <jza...@gmail.com> wrote:
Would `to.instantiateTypeParams(tparams, resArgs)` be more correct that `appliedType(to, resArgs: _*)`? It seems to handle some special cases for annotated/existential/higher-kinded types.

Probably. Any idea whether there's some good reason this distinction should exist? Is it handling cases I wouldn't want handled sometimes?

Not sure. instantiateTypeParams, This is Your Life:



I notice that appliedType routes to instantiateTypeParams if the type constructor is a polytype, which means if the symbol-application appliedType was

  appliedType(tyconSym.typeConstructor.normalize, args)

instead of

  appliedType(tyconSym.typeConstructor, args)

Then instantiateTypeParams would be called. I assume a bunch of unrelated pain would arrive as well.

Good to know.

I also wonder if we can write propagateKnownTypes more simply. I feel that a single subtype test should be sufficient (tvarType <:< from), rather than running through all of the base types.

It should be equivalent to the type inference that underlies:

class C[A, B]; class D[B, A] extends C[A, B]
def solve[A, B](da: D[A, B]): D[A, B] = ???
def s = solve(null) : C[Int, String]
def $ires1: C[Int,String] = (solve[String, Int](null): C[Int,String])

I'll try this out.

-jason

Jason Zaugg

unread,
Mar 27, 2013, 6:08:57 AM3/27/13
to scala-i...@googlegroups.com
On Wed, Mar 27, 2013 at 8:55 AM, Jason Zaugg <jza...@gmail.com> wrote:
I also wonder if we can write propagateKnownTypes more simply. I feel that a single subtype test should be sufficient (tvarType <:< from), rather than running through all of the base types.

It should be equivalent to the type inference that underlies:

class C[A, B]; class D[B, A] extends C[A, B]
def solve[A, B](da: D[A, B]): D[A, B] = ???
def s = solve(null) : C[Int, String]
def $ires1: C[Int,String] = (solve[String, Int](null): C[Int,String])

I'll try this out.

Okay, I see know that `propagateKnownTypes` is doing something more, as shown by the 'peer types' tests.

The extra knowledge comes from the fact that two independent classes can only be parents of some third class if their base types at any shared parent classes coincide. In the example below, if some type extends B1[Int], and it also extends C[?], then ? must also be Int.

scala> sealed trait A1[T]
defined trait A1

scala> trait B1[T] extends A1[T]
defined trait B1

scala> trait C1[T] extends A1[T]
defined trait C1

scala> object O extends B1[Int] with C1[String]
<console>:10: error: illegal inheritance;
 object O inherits different type instances of trait A1:
A1[String] and A1[Int]
       object O extends B1[Int] with C1[String]
              ^ 

-jason

Paul Phillips

unread,
Mar 27, 2013, 9:24:05 AM3/27/13
to scala-i...@googlegroups.com

On Wed, Mar 27, 2013 at 3:08 AM, Jason Zaugg <jza...@gmail.com> wrote:
Okay, I see know that `propagateKnownTypes` is doing something more, as shown by the 'peer types' tests.

Checkmate!

I'm going to take this morning to reacquaint myself with that beautiful test. What's a nice test like you doing in a place like this?

Paul Phillips

unread,
Mar 27, 2013, 9:42:06 AM3/27/13
to scala-i...@googlegroups.com
I'd love for you to continue scrutinizing that file though. How can we further develop propagateKnownTypes? What should it really be called, and where should it really be defined? To what extent can it be unified with baseType or asSeenFrom, so one need not even concern oneself with whether the symbol is from a supertype or a subtype?

Reply all
Reply to author
Forward
0 new messages