I'm trying to write a seemingly simple function, b...
# announcements
t
I'm trying to write a seemingly simple function, but Kotlin doesn't play dice, what am I doing wrong? declaration:
fun <E> Collection<E>.myContains(item: E) : Boolean
usage:
val isItInside: Boolean = listOf(1, 2).myContains("1")
expectation: usage fails to compile, because it's a
List<Int>
vs. a
String
https://stackoverflow.com/q/57224118/253468
s
What are you even trying to do?
E
gets inferred to be Int, so passing in a String is necessarily a compile-time error
What is the goal of your “myContains” method?
b
it's not a compile-time error because Collection is "out E"
t
@Shawn it says on SO "quite pointless", see the bottom for a full real life example
@Bob Glamm can you please elaborate? also is there a way to make it? in the full example on SO I have another class where I control the variance.
b
If you control the variance it works
Copy code
interface Container<E> {
    fun contains(item: E): Boolean
}

fun <X> Container<X>.myContains(item: X): Boolean = contains(item)

fun bar() {
    val q = object: Container<Int> {
        override fun contains(item: Int): Boolean {
            return item == 4
        }
    }
    q.myContains("1")
}
is correctly flagged as an error on the last line
r
Why not specify type of the collection explicitly, e.g.
listOf<Int>(1, 2)
b
doesn't make a difference: because Collection is covariant in E both of the following compile:
Copy code
fun <E> Collection<E>.myContains(item: E): Boolean = contains(item)

fun <E> itContains(coll: Collection<E>, item: E): Boolean = coll.contains(item)

fun foo() {
    val q: List<Int> = listOf(1, 2, 3)
    q.myContains("1")
    itContains(q, "1")
}
r
Ah, I see
t
@Bob Glamm So in
fun <E, T : Collection<E>> Asserter<T>.contain(item: E)
, even though I control the variance of
class Asserter<T>
, the upper bound of
T
is still a covariant
Collection
?
b
You'd have to ask a type expert, but I think that reasoning is correct
t
You sounded like one 😉, do you know someone?
b
Best bet is to hope the JB devs pop in and comment. But no, I'm not a type expert; I just happened to click into
kotlin.Collection
and noticed the variance annotation
t
Cheers!
b
🙂
k
It's pretty simple: you can't enforce this, which is why
kotlin.collections.contains
uses an internal annotation for this:
@kotlin.internal.OnlyInputTypes
.
Here's the issue for allowing us to use that annotation as well: https://youtrack.jetbrains.com/issue/KT-13198
t
oh 😞, thank you, subscribed
k
That funny. This does compile:
Copy code
fun <E> Collection<E>.myContains(item: E)
        = item in this
t
it's the usage that works unexpectedly
try to call it wrong
k
So I guess that's why it's not available, it violates a bunch of type stuff, it's just a trick.
Yeah I know.
t
but without it the language is weaker?
k
The point is that inlining this function to such a callsite doesn't compile anymore.
t
hmm
nope, it still compiles with
inline
k
I'm talking about really inlining it,
Ctrl+Alt+N
style.
I don't think
inline
actually every changes the behavior.
t
aaah 😄
g
I believe the compiler is flagging the error due to the fact that you are calling your extension function on a collection of ints and you are passing a parameter of type string.
You may try something like this:val isItInside: Boolean = listOf(1, 2).myContains(1)
d
Copy code
fun <T> Collection<T>.myContains(element: T): T = TODO()

interface A
interface B

// interface Collection<out E>
fun foo(x: Collection<A>, b: B) {
    x.myContains(b)
}
In call
x.myContains(b)
compiler should determines type of type variable
T
there is following constraints in this call:
Copy code
B <: T
Collection<A> <: Collection<T>
for that constraint system exists correct solution:
T == Any
it's correct since
Collection
has
out
variance, it's correct that
Collection<A> <: Collection<Any>
If you write similar example but instead
Collection
will use type invariant type parameter you will get correct type mismatch
t
@Glen you missed my point there (see OP), I would expect the compiler to flag it, but it doesn't. It compiles just fine and blows at runtime.
@dmitriy.novozhilov yeah, that's we figured as well, thanks for confirming. The lack of ability to write a function like this is sad 😞, is there no workaround here?
d
Unfortunately there is no good workaround, but we know about that problem and think about it https://youtrack.jetbrains.com/issue/KT-13198
👍 1
g
OK @twisterrob. Thanks for clarifying.