Can someone please explain why res2
in the REPL session shown below is inferred as List<Any>
? I thought since both Int
and Char
are Serializable
(evidenced by res0
and res1
), then res2
should also be list<Serializable>
. What is different about res2
?
[0] listOf('A', Pair('X', 'Y'))
res0: List<java.io.Serializable> = [A, (X, Y)]
[1] listOf(1, Pair('X', 'Y'))
res1: List<java.io.Serializable> = [1, (X, Y)]
[2] listOf(1, 'A')
res2: List<Any> = [1, A]
The type of listOf(1, 'A')
should be a List
of an intersection type between Comparable<*>
and Serializable
. After all, Int
and Char
are also Comparable
.
fun main() {
val x = listOf(1, 'A')
// both of these compile
f(x)
g(x)
}
fun f(x: List<Serializable>) {}
fun g(x: List<Comparable<*>>) {}
It is important that the x
above is declared in a statement scope (See scopes), in the body of main
. If it had been declared in a declaration scope (such as the top level),
val x = listOf(1, 'A') // this is now in a declaration scope
fun main() {
// now none of these compile!
f(x)
g(x)
}
fun f(x: List<Serializable>) {}
fun g(x: List<Comparable<*>>) {}
This is because the type of a non-local property like x
cannot contain non-denotable types (e.g. intersection types), and so type approximation is applied. Type approximation replaces the intersection type with the GLB of Serializable
and Comparable<*>
, which is Any
.
Something similar is happening in the REPL. You can think of the expressions you enter into the REPL as being assigned to global properties called res0
, res1
, etc. The type that the REPL produces as output is kind of like printing ::res0.returnType
. There is no KType
that can represent a intersection type, so the intersection type must be approximated.
Even if you write val x = listOf(1, 'A')
, the type of x
will still be List<Any>
, because the REPL treats this declaration similar to a top-level declaration like in the code snippet above. This can be shown by the fact that smart casts are not possible on top level var
s:
>>> var x: Int? = null
>>> if (x != null) { println(x.toLong()) }
error: smart cast to 'Int' is impossible, because 'x' is a mutable property that could have been changed by this time
if (x != null) { println(x.toLong()) }