Reputation: 8539
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
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
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