Reified generics


#1

Hi all,

Back in February Alex posted a note about getting rid of TypeInfo, I was wondering what were your plans for reified generics.

Thanks in advance

F.


#2

For now we have postponed reified generics until better times: they make the Java interop ugly: passing the typeinfo stuff everywhere.


#3

In which case, could there be a compiler error if someone calls javaClass<GENERIC_PARAM>() ?

e.g.:

  fun name<T>() = javaClass<T>().toString()

  println(name<Double>())
  println(javaClass<Double>().toString())
prints: class java.lang.Object double

Yikes!

With regard to the real-world, it means that this rather nice example doesn’t work:
http://java.dzone.com/articles/kotlin-guice-example

Rather than waiting for a generalised solution to reified generics, is there any possibility of a short-term workaround?
e.g. something like an annotation to tell the compiler to pass typeinfo as a hidden parameter?
then in the few special cases where I want reified generics, I can ensure I have added the appropriate annotation to make my generic arg ‘reify-safe’

something like:

  fun name<[reify] T>() = javaClass<T>().toString()

which the compiler turns into something like:
  fun name<[reify] T>(reifiedT : Class<T>) = reifiedT.toString()

and any callsite of a function with the [reify] annotation into
  println(name<Double>(reifiedT = javaClass<Double>()))

This even works across chained method calls (as long as the [reify] annotation is present all the way down):

  fun printName<[reify] T>() = print(name<T>())

also transforms to:   fun printName<[reify] T>(reifiedT : Class<T>) = print(name<T>(reifiedT = reifiedT))
Maybe Kotlin already has enough annotation processing support to let me easily do this myself ?

By the way, Kotlin is shaping up really nicely. I gave M3 a try, but needed the fix to KT-2686 for any sort of java interop, so am now on nightlies :slight_smile:
Good job!


#4

The main pain of not having reified generics is having to explicitly pass in the Class<T> objects in APIs which need the class of the generic parameter (e.g. JPA, Camel etc). its fairly common in many frameworks).

See this discussion: http://youtrack.jetbrains.com/issue/KT-1751 for how we could do it. I really like Andrey’s idea about using a reified annotation with inline functions to ensure that the function can only be called when the type parameter can be completely reified etc.

So kotlin framework/API writers could write inline helper/extension methods to avoid having to pass in the extra class parameters; which can only be used when the T is known at the call site.

e.g. using JPA’s EntityManager we could do…

inline fun <reified T> EntityManager<T>.find(primaryKey: Any): T? {   return find(javaClass<T>, primaryKey) }


val customer = em.find<Customer>(“abc”)
// customer is Customer? now

// or updating a value in place
order.customer = em.find(“abc”)!!

// this would not be allowed
fun <T> noRefiy() {
  // T is not reified so compile error
  em.find<T>(“abc”)
}

The thing I really like about this suggestion is its very lightweight & simple and avoids confusion. Sure folks could write bad long methods that get inlined causing huge bytecode bloat; but then anyone can write bad code in any language :). It coudl be a compile warning if there's more than 5 statements or something :)


#5

Hah - thanks, didn't see that one. I see you've basically discussed every point in the post already (I had also tried an inline fun as a workaround) :)

I’m guessing there’s no sort of annotation processing tool yet? I may be tempted to grab the Kotlin compiler source and see if I could add the annotation myself.
What’s Andrey’s main concern about supporting this in non-inline functions? Is it just that fiddling with method sigs is generally avoided?


#6

Supporting reified generics for (non-inline) function is not really a solution: we'd need to go all the way and support the same for classes, too. This is a huge pain for Java interop, and I'm not convinced that the benefits we could get this way are worth it.


#7

In that case it sounds like the inline annotated reify is a good compromise. I definitely think there should be a compiler error for javaClass<GENERIC>()

When you say a huge problem for java interop, I assume you’re talking about calling a reified kotlin function from java?
Surely if java code needed to call a [reify] annotated Kotlin function, the java code would just ‘see’ the extra function argument of type Class<T>, and would have to provide a value?
Or were you trying to avoid having different method signatures from a java perspective?

BTW - Kotlin & Leda are doing amazingly well at fast code completion and compile. Nice one!


#8

Totally agree about the error.

The problem is that if we reify parameters of classes, a java.lang.Class<T> object is not enogh any more, and constructing your own TypeInfo object in Java is problematic


#9

I obviously haven't done enough thinking about reification. To speed me on my way, have you got a really quick example where a field of type Class<T> is not enough for reified calls?

Of course the only thing you can do with a reified arg in kotlin is eventually call: javaClass<[reify] X>()
which is pretty sweet, because by the “add extra arguments” approach, any call to that would be transformed into:
  javaClass<[reify] X>(reifyX: Class<X>), which is identity :slight_smile:

ooo, and if the compiler checked for an existing method with the correct signature, then it wouldn’t generate one, and then javaClass is no longer a ‘magic’ function.

I must be missing something.


#10

I really like Andrey's idea about using a reified annotation with inline functions to ensure that the function can only be called when the type parameter can be completely reified etc.

With all due respect for Andrey and the amazing job he's doing with Kotlin, this looks like an idea that was originally proposed by Neal in 2006 for Java (source:  http://gafter.blogspot.com/2006/11/reified-generics-for-java.html):

The other approach modifies the language so that the declaration of a generic parameter distinguishes reified type parameters from erased ones. It is a pure extension of the language. The existing syntax for generics would continue to designate generics by type erasure, but a newly introduced syntax would be used to declare reified type parameters. Perhaps something like this:

class NewCollection<class E> extends Collection<E> { ... }
class NewList<class E> extends NewCollection<E>, List<E> { ... }