David Holas
01/12/2024, 1:29 PMfun <Class, Value> store(property: KProperty1<Class, Value>, value: Value) {
}
data class Test(
val name: String,
)
store(Test::name, 1) // This should be illegal cause name is String no?
David Kubecka
01/12/2024, 1:36 PMValue
is inferred to Any
. I recently stumbled upon that myself and wondered whether there's an easy way to see the inferred types in these kinds of situations.David Holas
01/12/2024, 1:42 PMCLOVIS
01/12/2024, 1:43 PMValue
as a return of your original function to force IntelliJ to see it.CLOVIS
01/12/2024, 1:44 PMAny
(the compiler found something slightly more specific), but for all practical purposes that is indeed what is happening.CLOVIS
01/12/2024, 1:48 PMIs there a way to prevent that?I don't see a way for this example. Maybe if you have a more complete case of what you are trying to do?
Adam S
01/12/2024, 2:13 PMDavid Holas
01/12/2024, 2:18 PMval filter: Filter<Test> = Filter
.where(Test::name, equals = "Some name")
.where(Test::number, equals = 17)
I had to change it a bit, but I have this:
// Can filter only the types that implement this
sealed interface ServiceData<out This> where This: ServiceData<This>
sealed interface AnyFilter<out Data : ServiceData<Data>>
class Filter<out Data : ServiceData<Data>> : AnyFilter<Data>
data class CombinedFilter<Data: ServiceData<Data>>(
val filter1: AnyFilter<Data>,
val filter2: AnyFilter<Data>,
) : AnyFilter<Data>
data class EqualsFilter<Data : ServiceData<Data>, out Value>(
val field: KProperty1<Data, Value>,
val value: Value,
) : AnyFilter<Data>
fun <Data : ServiceData<Data>, Value> AnyFilter<Data>.where(field: KProperty1<Data, Value>, equals: Value): AnyFilter<Data> =
CombinedFilter(this, EqualsFilter(field, equals))
fun <Data: ServiceData<Data>> processFilter(type: KClass<Data>, filter: AnyFilter<Data>) {
return when (filter) {
is Filter -> { }
is CombinedFilter<Data> -> {
processFilter(type, filter.filter1)
processFilter(type, filter.filter2)
}
is EqualsFilter<Data, *> -> {
/*
Send to somewhere for comparison: filter.field.name, filter.value (I would actually wanna get the SerialName here but this is the best I can do)
*/
}
}
}
And it then do:
data class Test(
val name: String,
val number: Int,
) : ServiceData<Test>
processFilter(Test::class, filter = Filter<Test>()
.where(Test::name, "Some name")
.where(Test::number, 17)
)
Now that I have written it, I dont think its really much relevant, but I dont wanna discard the post nowCLOVIS
01/12/2024, 2:18 PMimport kotlin.reflect.KProperty1
data class Foo(
val name: String,
)
@JvmInline value class PropertyWrapper<T>(val property: KProperty1<*, T>) {
infix fun store(value: T) {
TODO("your original function here")
}
}
fun <T> into(property: KProperty1<*, T>) = PropertyWrapper(property)
into(Foo::name) store 1 // does not compile
It works by binding the type parameter to the value in a first function. When the second function is called, the type parameter is already bound, so the compiler cannot infer anything else.CLOVIS
01/12/2024, 2:20 PM@JvmInline value class PropertyWrapper<T>(val property: KProperty1<*, T>) {
infix fun set(value: T) {
TODO("your original function here")
}
}
val <T> KProperty1<*, T>.storage get() = PropertyWrapper(this)
Foo::name.storage set "test"
Foo::name.storage set 1
David Holas
01/12/2024, 2:21 PMCLOVIS
01/12/2024, 2:22 PMFilter
.where(Foo::name) isEqualTo yourValue
where the where
can emit a similar kind of wrapperDavid Holas
01/12/2024, 2:23 PM.where(Foo::name.storage, equals = "Some")
CLOVIS
01/12/2024, 2:25 PMWhy do I need to use infix?You don't, I just find it nicer 🙂 What you have to do is have a first function that binds the type parameter into its return type. When the second function is called, it is bound already, so it cannot be up-casted.
David Kubecka
01/12/2024, 2:26 PMassertEquals
. Is there any way how to see what the compiler thinks in such scenarios?CLOVIS
01/12/2024, 2:26 PMKProperty1
is out
, so KProperty1<Foo, Int>
is a subtype of KProperty1<Foo, Any>
. My trick is simply to use a wrapper that is invariant, such that they are not subtype of each other anymore.CLOVIS
01/12/2024, 2:27 PMI think this would work? I'll let you try itCopy code.where(Foo::name.storage, equals = "Some")
David Holas
01/12/2024, 2:52 PMBut im gonna stick with something like this:Copy code.where(Foo::name.storage, equals = "Some")
.where(Foo::name.value equalTo "Some")
CLOVIS
01/12/2024, 2:53 PMDavid Holas
01/12/2024, 3:08 PMvalue
directly to the instance of a wrapper created from the KProperty1
. And that is not possible to do inside a single functionDavid Holas
01/12/2024, 4:24 PMasdf asdf
01/12/2024, 4:29 PM@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
fun <Class, Value> store(property: KProperty1<Class, @kotlin.internal.Exact Value>, value: Value) {
}
data class Test(
val name: String,
)
fun main() {
store(Test::name, "hello") // Compiles
store(Test::name, 11) // Compile Error: Type mismatch
}
David Holas
01/12/2024, 4:35 PMinternal
itself