Last active
August 29, 2015 14:23
-
-
Save udalov/e7217a39f48821b67bda to your computer and use it in GitHub Desktop.
Sketch of a more type-safe reflection API for Kotlin
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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