Self Types


#21

Yes it will have erased types in addition to generic types (as the case will all other generic methods). Take the following example:

open class KtParent { // Class in Kotlin with self-typed property
    val child:Self
}

class KtChild: KtParent()

The function KtParent.getChild():KtParent is generated on KtParent
A synthetic function KtChild.getChild():KtChild is generated on KtChild

class JavaChild extends KtParent { }

No KtChild KtChild.getChild() is generated in Java.

In the case that KtParent is created with a generic parameter representing the self type you are in recursive generics land with all the major issues that entails (beyond the fact that recursive generics cannot express the full semantics of a Self type). The problem here is never runtime, it is always compile time and correct code being generated from Java.

Method bridging would be the expected implementation for covariant return types, but indeed it may be possible to replace it with checkCast at the call site instead. Of course all this (with or without bridging) means that the contract can be violated in Java without warning (there is nothing in Java to allow specifying meaning). Synthetic methods would be needed for correct usage from Java (the Java compiler does not understand it needs to inject a checkCast and treat the value as a subtype).

The difference with declaration site variance is that it is more of a syntactic sugar that removes the need to specify variance at use site. The variance is still specified at use on a binary level, so it is impossible for Java to violate Kotlin classes, even if it is able to use the type itself with different variance.


#22

Are we talking about JVM interopability or Java interopability? If it is just JVM interopability, it is not really that complicated, since everything is just java.lang.Object with unchecked type casts. As for the List example, it is even easier, because lists don’t contain type in the runtime anyway, so we don’t need to care about the List T type at all.

Looks like what really matters is the Java interopability. But are there really that many people calling Kotlin from Java (with the API also in Kotlin)? It isn’t really a big problem if the generated class files force Java context to do unnecessary casts as if there were no generics.


#23

The problem is that call from java is allowed. This means that someone could call a kotlin function from java and do something that is prohibited in kotlin, thus creating an undebugable error.

I think that the solution to those problems is to add a compiler flag that somehow forbids to call kotlin code from java (package name mangling or something). In this case one can implement some kotlin-only features without fear of backward compatibility problems.


#24

Sounds like the same solution to many other problems like inline functions accessing private methods.


#25

A synthetic function KtChild.getChild():KtChild is generated on KtChild

I contend there is no need for synthetic methods and the like. The function on KtParent can be generated to include type information:

KtParent.getChild():KtParent <~~~ compiler annotates with self

As such the compiler can always infer the self type; there is no need for synthetics.

As for Java, again it can override methods employing self from Kotlin classes. From the Java perspective there is no self type, therefore Java code must cast where necessary.


#26

I note the Manifold project for Java advertises self types:

http://manifold.systems/docs.html#the-self-type

I wonder what they are doing under the hood to make this work?

They also say they support non-Java JVM languages (with some help):

http://manifold.systems/docs.html#use-with-other-jvm-languages