Home > front end >  Scala 2 type inference
Scala 2 type inference

Time:12-24

def test[T: ClassTag]: T = {
  println(classTag[T])
  null.asInstanceOf[T]
}
val x1: Int = test
val x2: Int = test[Int]

prints

Nothing
Int

I would expect the compiler to guess the Int type without the need to provide it explicitly (EDIT: on the right hand side, i.e. make val x1: Int = test work).

Is there perhaps a workaround to get rid of the explicit type annotation?

CodePudding user response:

I suspect that compiler can't infer Int in val x1: Int = test[???] because both options:

val x1: Int = test[Int] // returns Int

and

val x1: Int = test[Nothing] // returns Nothing <: Int

are valid. So compiler just has to guess what option you meant.

When compiler has a choice it often selects the minimal type out of the options. And currently this is Nothing.

Why Scala Infer the Bottom Type when the type parameter is not specified?


In principle, if you'd like to explore the type of left hand side you can make test a macro. Then it can be even not generic. Making it whitebox means that it can return a type more precise than declared (Any) e.g. Int.

import scala.reflect.macros.whitebox // libraryDependencies  = scalaOrganization.value % "scala-reflect" % scalaVersion.value
import scala.language.experimental.macros

def test: Any = macro testImpl

def testImpl(c: whitebox.Context): c.Tree = {
  import c.universe._
  val T = c.internal.enclosingOwner.typeSignature
  println(s"T=$T")
  q"""
    _root_.scala.Predef.println(_root_.scala.reflect.runtime.universe.typeOf[$T])
    null.asInstanceOf[$T]
  """
}
// in a different subproject

val x1: Int = test // prints at runtime: Int

  // scalacOptions  = "-Ymacro-debug-lite"
//scalac: T=Int

//scalac: {
//  _root_.scala.Predef.println(_root_.scala.reflect.runtime.universe.typeOf[Int]);
//  null.asInstanceOf[Int]
//}

CodePudding user response:

Actually there's nothing wrong with type inference here, type inference here means that the compiler should figure out that the T type parameter is Int, and the returned value from your method expression is an integer, and that's working properly:

x1: Int = 0 // this is the result of your first line

You can also try printing this:

println(x1.getClass) // int

What's not working as expected is implicit resolution for a generic type "ClassTag[_]". The reason it prints Nothing is that the compiler found the object scala.reflect.ClassTag.Nothing : ClassTag[Nothing] suitable for your case. Now about this new thing, there are loads of content on SO and the internet for why this happens and how to deal with it in different cases.

Here's another piece of code to differentiate type inference with type erasure in your case:

def nullAs[T]: T = null.asInstanceOf[T]

val int: Int = nullAs // 0: Int

// type erasure:
case class Container[T](value: T)
implicit val charContainer: Container[Char] = Container('c')

def nullWithContainer[T](implicit container: Container[T]): T = {
  println(container)
  null.asInstanceOf[T]
}

val long: Long = nullWithContainer
// prints Container(c)
// 0L

Which means type inference is done correctly, but type erasure has happened, because in runtime, the type of charContainer is Container, not Container[Char].

CodePudding user response:

val x1: Int = test

In this line, the Int you have given is the type for the value x1 which would hold the result of the function test and as for the classTag of T the compiler finds no information which is why it returns Nothing, in which case its taking val x1: Int = test[Nothing] so you would always have to mention a Type for test else it would print Nothing

  • Related