0
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?

5
  • You are probably expecting wrong things. As your question does not clarify what you were expecting and how Scala is not matching your expectations, you will have to add clear details. Commented Dec 23, 2022 at 12:08
  • From what I can see, Scala can perfectly infer the type of x2 as Int in val x2 = test[Int] even without explicitly saying val x2: Int. What exactly is not working for you ? Commented Dec 23, 2022 at 12:13
  • 2
    I would expect Scala to infer the type paramter T == Int (not Nothing ) in the line val x1: Int = test Commented Dec 23, 2022 at 12:20
  • You can get what you expected in Scala3 ^_^,there are many enhance of type inference in Scala3. Commented Dec 25, 2022 at 15:06
  • 1
    @Eastsun Well, the difference with Scala 3 is now that in Scala 3 Nothing doesn't have class tag scastie.scala-lang.org/DmytroMitin/OAEoGDbmTiafBCrK5KGcmw/3 while in Scala 2 it does scastie.scala-lang.org/DmytroMitin/OAEoGDbmTiafBCrK5KGcmw/4 Commented Dec 28, 2022 at 13:32

3 Answers 3

5

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] //} 
Sign up to request clarification or add additional context in comments.

3 Comments

Yeah this one also makes complete sense, I tried implementing a more verbose version of the same exact method like this: def testImpl[T, U >: T : ClassTag, L <: T : ClassTag]: T = { println(classTag[L]); println(classTag[U]; null.asInstanceOf[T] }, and then delegating the implementation is test[T] method to testImpl[T, T, T]. And what I saw is that both upper bound and lower bounds of T are Nothing, so it makes sense that T is also Nothing!
I guess at least in Scala2, this problem cannot be solved, it should be handled with alternatives such as Either or something. @KamilKloch
@AminMal Well, I solved it eventually but with macros.
2

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].

Comments

0
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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.