I have stumbled upon an internal annotation that I...
# stdlib
o
I have stumbled upon an internal annotation that I think I would also like to use... In my situation, I want to declare a fun
holds
for a DSL that is a generic function very similar to
in
- it checks the contents of something against another value. My current implementation looks like this:
Copy code
@TransformationDsl
    infix fun <V> ValueProvider<S, V>.holds(value: V): Boolean = value in this
The problem is, written like this, the user loses type safety as the generic
V
will always look for the first suitable "upper" type, being
Any
in the worst case. So, usage like this is not prohibited by the compiler:
Copy code
val x: Value<String> = TODO()
if (x holds 42) {
  // ...
}
In this case, I want the compiler to note that the Integer 42 can never be contained in the
Value<String>
! Looking at the declaration of the
operator fun in
for an Iterable reveals this hack:
Copy code
public operator fun <@kotlin.internal.OnlyInputTypes T> Iterable<T>.contains(element: T): Boolean {
    if (this is Collection)
        return contains(element)
    return indexOf(element) >= 0
}
If you try to check whether an Iterable of String contains an Int value, the compiler does not allow this. I want exactly the same behaviour, but I cannot use the annotation, because it is internal... Any other way to not allow the generic V to be allowed to be Any? Thanks!
d
Have you tried using variance? I would try using
out V
in the function to signify that only subtypes are allowed. I would also consider using declaration-site variance in the ValueProvider class
o
@Dan Rusu variance parameters are not allowed on functions and my ValueProvider is declared
ValueProvider<out V>
already. That does not help.
Here is a complete reproducer and showcase what I want:
Copy code
interface ValueProvider<out V> {
    val value: V
}

infix fun <V> ValueProvider<V>.holds(value: V) = this.value == value

fun demoValueProvider(v: ValueProvider<Int>) {
    if (v holds "42") { // this should not work
        println("Value is 42")
    } else {
        println("Value is not 42")
    }
}

fun demoList(l: List<Int>) {
    if ("42" in l) { // rightfully fails to compile
        println("List contains 42")
    } else {
        println("List does not contain 42")
    }
}
I want to have the same behaviour as seen with the
in
operator. The error you see on the
demoList
fun is:
Type inference failed. The value of the type parameter 'T' must be mentioned in input types (argument types, receiver type, or expected type). Try to specify it explicitly.
Which is hinting me at that the
@kotlin.internal.OnlyInputTypes
is doing this. But this annotation is internal. 😞
c
I have the same problem in my DSLs, please vote for https://youtrack.jetbrains.com/issue/KT-79654/Receiver-bound-type-parameters and add your example 🙏