Tomer Shetah
Tomer Shetah

Reputation: 8539

How does Scala resolve generic types automatically?

Please consider the following 2 lines of code:

def foo[F[B],A](fa: F[A]): String = fa.toString
println(foo(10))

It prints 10. What I wonder, is how it even compiles. I am trying to understand how Scala resolves the generics here. I mean, what are A, B, and F?

When looking at Intellij, it seems like 10 is converted from a Scala Int, to a Java Integer. But I don't know how the Java Integer is converted to any F[A] (Java Integer is not a generic type), as foo expects to receive.

Upvotes: 3

Views: 207

Answers (2)

Mario Galic
Mario Galic

Reputation: 48420

how the Java Integer is converted to any F[A](Java Integer is not generic type), as foo expects to receive.

Java Integer is subtype of Comparable[Integer]

public final class Integer extends Number implements Comparable<Integer>

Scala 2.13 infers F = Any and A = Nothing

scala> def foo[F[_],A](fa: F[A]) = fa
def foo[F[_], A](fa: F[A]): F[A]

scala> foo(10) // print
foo[Any, Nothing](10) // : Any
scala> foo(10)
val res9: Any = 10

This inference happens because of the presence of implicit conversion

implicit def int2Integer(x: Int): java.lang.Integer = x.asInstanceOf[java.lang.Integer]

If we hide this conversion it will not compile

scala> implicit val int2Integer = null
val int2Integer: Null = null

scala> foo(10)
       ^
       error: no type parameters for method foo: (fa: F[A]): F[A] exist so that it can be applied to arguments (Int)
        --- because ---
       argument expression's type is not compatible with formal parameter type;
        found   : 10
        required: ?F[?A]
           ^
       error: type mismatch;
        found   : Int(10)
        required: F[A]

However note despite the fact that int2Integer made it compile, the implicit conversion did not actually take place (which might be a bug)

scala> foo(10)
val res9: Any = 10

where res9 should have been typed as Comparable[Integer] instead of Any.

Scala 3 (Dotty) infers F = Comparable and A = Integer as per -Xprint:typer

foo[Comparable, Integer](int2Integer(10))

and the implicit conversion is indeed applied

Starting dotty REPL...
scala> def foo[F[_],A](fa: F[A]) = fa
def foo[F[_$1], A](fa: F[A]): F[A]

scala> foo(10)
val res0: Comparable[Integer] = 10

Upvotes: 4

francoisr
francoisr

Reputation: 4595

If you're using Scala 2.12 or previous, there is an implicit conversion from Int to RichInt in order to provide additional methods on Java's Int.

The definition looks like

final class RichInt extends AnyVal with ScalaNumberProxy[Int] with RangedProxy[Int] 

So here F[A] could be ScalaNumberProxy[A] with RangedProxy[A] and B would be Int. Here is an explicit example using only ScalaNumberProxy for simplicity.

import scala.runtime.ScalaNumberProxy
def foo[F[B],A](fa: F[A]): String = fa.toString
println(foo[ScalaNumberProxy, Int](10))

If using Scala 2.13+, RichInt appears to have been renamed, but there is certainly a similar implicit conversion that applies.

Upvotes: 1

Related Questions