Type inference: FROM variable's declared type TO type of initialization expression?

22 views
Skip to first unread message

Daniel Barclay

unread,
Jul 24, 2016, 9:28:48 PM7/24/16
to scala-user
Can the declared type of a variable (val or var) affect the type
of the expression used to initialize or assign to the variable?


I thought that if I declared a method (somewhat) like this:

def getViaTag[C](implicit classTag: ClassTag[C]): C = ...

and declared/initialized a variable like this:

val v: SpecificClass = getViaTag

then the type of that initialization expression "getViaTag" would
be SpecificClass (because that's what that type has to be to for
the assignment to be valid (and because getViaTag is generic
enough to return type SpecificClass)).

However, when I try that, the method only gets the class tag for
type Nothing, implying that the type of that initialize expression
is just Nothing.


Was I confused in thinking that type inference could propagate the
declared type of variables "down" into some initialization
expressions (not just up from the expressions to the variables)?

Or am I just not using type tags the right way?

Thanks,
Daniel

P.S. Here is my full example code:


package com.us.dsb.explore.tags

import scala.reflect.ClassTag

object TryInferringFromContext {

class GeneralThing
case class SpecificThingOne() extends GeneralThing
case class SpecificThingTwo() extends GeneralThing {
def someMethod(): Unit = {}
}

val things: List[GeneralThing] = List(SpecificThingOne(), SpecificThingTwo())

//import System.err.println
def getThingViaTags[C <: GeneralThing](implicit classTag: ClassTag[C]): Option[C] = {
println(s"classTag = $classTag")
val requestedClass = classTag.runtimeClass
val anyFirstMatch = things.filter(obj => requestedClass.isInstance(obj)).headOption
anyFirstMatch.map(o => o.asInstanceOf[C])
}


def main(args : Array[String]): Unit = {

// As expected/wanted, passed class tag is for SpecificThingOne:
val v1 /*: Option[SpecificThingOne]*/ = getThingViaTags[SpecificThingOne]

// Not as expected/wanted, passed class tag is for Nothing:
val v2: Option[SpecificThingOne] = getThingViaTags
// Why is getThingViaTags's C inferred to be type Nothing?
// Does type inference ever propagate the declared type _from_ a type
// ascription on a variable declaration "down" _to_ the initialization
// expression? If it does, why doesn't it do so here?

// Not as expected/wanted, class class tag is for Nothing:
def takesSpecificType(arg: Option[SpecificThingTwo]): Unit = {}
val v3 = takesSpecificType(getThingViaTags)
}

}

Rodrigo Cano

unread,
Jul 25, 2016, 11:09:10 PM7/25/16
to Daniel Barclay, scala-user
I never quite understood why this work, but it does:

scala> import scala.annotation.unchecked.uncheckedVariance
import scala.annotation.unchecked.uncheckedVariance

scala> type ClassTag[+T] = scala.reflect.ClassTag[T@uncheckedVariance]
defined type alias ClassTag

scala> def infer[T: ClassTag]: T = {println(implicitly[ClassTag[T]]); null.asInstanceOf[T]}
infer: [T](implicit evidence$1: ClassTag[T])T

scala> val i: Int = infer
Int
i: Int = 0

scala> val s: String = infer
java.lang.String
s: String = null

Basically, the key is that the implicit type class of T has to be other than invariant, and even creating an alias type works for that purpose.

Cheers.



--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply all
Reply to author
Forward
0 new messages