Skip to content

Instantly share code, notes, and snippets.

@udalov
Last active August 29, 2015 14:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save udalov/e7217a39f48821b67bda to your computer and use it in GitHub Desktop.
Save udalov/e7217a39f48821b67bda to your computer and use it in GitHub Desktop.
Sketch of a more type-safe reflection API for Kotlin
@file:suppress("UNCHECKED_CAST", "BASE_WITH_NULLABLE_UPPER_BOUND", "UNUSED_PARAMETER")
interface KElement
// Class, package, type
interface KDeclarationContainer : KElement {
val allMembers: Collection<KElement>
}
interface KClass<T : Any> : KDeclarationContainer {
// ...
}
interface KPackage : KDeclarationContainer {
// ...
}
interface KType<T> {
// ...
}
// Callables: function, property
interface KCallable<T : Any, out R> : KElement {
fun call(instance: T?, vararg args: Any?): R
val parameters: List<KParameter>
val instanceType: KType<T>?
// ...
}
interface KParameter {
val name: String?
val index: Int
val type: KType<*>
val isExtensionReceiver: Boolean
// ...?
}
// TODO: also store extension receiver type for functions?
interface KFunction<T : Any, out R> : KCallable<T, R> {
// ...
}
// TODO: E can be 'Nothing' or 'Nothing?' itself, is it OK?
interface KProperty<T : Any, E, out R> : KCallable<T, R> {
val getter: Getter<T, E, R>
fun get(instance: T?, extensionReceiver: E?): R
interface Getter<T : Any, E, out R> : KFunction<T, R> {
val property: KProperty<T, E, R>
}
}
interface KMutableProperty<T : Any, E, R> : KProperty<T, E, R> {
val setter: Setter<T, E, R>
fun set(instance: T?, extensionReceiver: E?, value: R)
interface Setter<T : Any, E, R> : KFunction<T, R> {
val property: KMutableProperty<T, E, R>
}
}
// -----------------------------------------------------------
// Extensions for obtaining declarations of specific sort
inline fun <reified T : KElement> KDeclarationContainer.members(): Collection<T> =
allMembers.filterIsInstance<T>()
val KClass<*>.staticFunctions: Collection<KFunction<Nothing, *>>
get() = members<KFunction<*, *>>().filter { !it.hasInstance } as Collection<KFunction<Nothing, *>>
val <T> KClass<T>.memberFunctions: Collection<KFunction<T, *>>
get() = members<KFunction<*, *>>().filter { it.hasInstance } as Collection<KFunction<T, *>>
val KClass<*>.staticProperties: Collection<KProperty<Nothing, Nothing, *>>
get() = members<KProperty<*, *, *>>().filter { !it.hasInstance && !it.hasExtensionReceiver } as Collection<KProperty<Nothing, Nothing, *>>
val <T> KClass<T>.memberProperties: Collection<KProperty<T, Nothing, *>>
get() = members<KProperty<*, *, *>>().filter { it.hasInstance && !it.hasExtensionReceiver } as Collection<KProperty<T, Nothing, *>>
val <T> KClass<T>.memberExtensionProperties: Collection<KProperty<T, *, *>>
get() = members<KProperty<*, *, *>>().filter { it.hasInstance && it.hasExtensionReceiver } as Collection<KProperty<T, *, *>>
val KPackage.freeFunctions: Collection<KFunction<Nothing, *>>
get() = members<KFunction<*, *>>().filter { !it.hasExtensionReceiver } as Collection<KFunction<Nothing, *>>
val KPackage.extensionFunctions: Collection<KFunction<*, *>>
get() = members<KFunction<*, *>>().filter { it.hasExtensionReceiver }
val KPackage.variables: Collection<KProperty<Nothing, Nothing, *>>
get() = members<KProperty<*, *, *>>().filter { !it.hasExtensionReceiver } as Collection<KProperty<Nothing, Nothing, *>>
val KPackage.extensionProperties: Collection<KProperty<Nothing, *, *>>
get() = members<KProperty<*, *, *>>().filter { it.hasExtensionReceiver } as Collection<KProperty<Nothing, *, *>>
// Convenience get/set for properties with at least one receiver missing
fun <R> KProperty<Nothing, Nothing, R>.get(): R = get(null, null)
@kotlin.platform.platformName("memberGet")
fun <T : Any, R> KProperty<T, Nothing, R>.get(instance: T): R = get(instance, null)
@kotlin.platform.platformName("extensionGet")
fun <E, R> KProperty<Nothing, E, R>.get(extensionReceiver: E): R = get(null, extensionReceiver)
fun <R> KMutableProperty<Nothing, Nothing, R>.set(value: R) = set(null, null, value)
@kotlin.platform.platformName("memberSet")
fun <T : Any, R> KMutableProperty<T, Nothing, R>.set(instance: T, value: R) = set(instance, null, value)
@kotlin.platform.platformName("extensionSet")
fun <E, R> KMutableProperty<Nothing, E, R>.set(extensionReceiver: E, value: R) = set(null, extensionReceiver, value)
// Transform property accessors to type-safe functions
fun <R> KProperty.Getter<Nothing, Nothing, R>.asFreeFunction(): () -> R =
fun(): R = property.get()
fun <T : Any, R> KProperty.Getter<T, Nothing, R>.asMemberFunction(): (T) -> R =
fun(instance: T): R = property.get(instance)
fun <E, R> KProperty.Getter<Nothing, E, R>.asExtensionFunction(): (E) -> R =
fun(extensionReceiver: E): R = property.get(extensionReceiver)
fun <T : Any, E, R> KProperty.Getter<T, E, R>.asMemberExtensionFunction(): (T, E) -> R =
fun(instance: T, extensionReceiver: E): R = property.get(instance, extensionReceiver)
fun <R> KMutableProperty.Setter<Nothing, Nothing, R>.asFreeFunction(): (R) -> Unit =
fun(value: R) = property.set(value)
fun <T : Any, R> KMutableProperty.Setter<T, Nothing, R>.asMemberFunction(): (T, R) -> Unit =
fun(instance: T, value: R) = property.set(instance, value)
fun <E, R> KMutableProperty.Setter<Nothing, E, R>.asExtensionFunction(): (E, R) -> Unit =
fun(extensionReceiver: E, value: R) = property.set(extensionReceiver, value)
fun <T : Any, E, R> KMutableProperty.Setter<T, E, R>.asMemberExtensionFunction(): (T, E, R) -> Unit =
fun(instance: T, extensionReceiver: E, value: R) = property.set(instance, extensionReceiver, value)
// TODO: convenient analogue of call() for calling free functions?
// Can be named "call" only if KCallable#call takes an array, not a vararg :(
// Bind
fun <T : Any, R> KFunction<T, R>.bindInstance(instance: T): KFunction<Nothing, R> = todo()
fun <T : Any, E, R> KProperty<T, E, R>.bindInstance(instance: T): KProperty<Nothing, E, R> = todo()
fun <T : Any, E, R> KProperty<T, E, R>.bindExtensionReceiver(extensionReceiver: E): KProperty<T, Nothing, R> = todo()
// Other useful extensions
val KCallable<*, *>.extensionReceiverType: KType<*>?
get() = parameters.firstOrNull { it.isExtensionReceiver }?.type
val KCallable<*, *>.hasInstance: Boolean
get() = instanceType != null
val KCallable<*, *>.hasExtensionReceiver: Boolean
get() = extensionReceiverType != null
// -----------------------------------------------------------
fun todo() = error("Not implemented")
fun eat<T>(s: T) = println(s)
fun test(
p0: KMutableProperty<Nothing, Nothing, String>,
p1: KProperty<Nothing, String, Int>
) {
eat<String>(p0.get())
eat<String>(p0.getter.asFreeFunction()())
p0.set("")
p0.setter.asFreeFunction()("")
val p: KProperty<Nothing, Nothing, Int> = p1.bindExtensionReceiver("")
eat<Int>(p.get())
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment