Why are there two indexOfFirst functions declared for Iterable and List types?


#1

Hi, it is the first time for me to ask questions in this discussion forum so if there is anything I miss please let me know so I can edit my topic.

I am curious about the fact that Kotlin API declares the same extension function IndexOfFirst, which has the exact same logic, for multiple types of classes: in this case, Iterable and List types.
Below is the link for the code that I am referring to:

I simply thought having one function declared into Iterable would be suitable enough, so I just wonder if there is specific reason why it is declared in two places.

Thanks in advance!

Shohei


#2

Maybe it has something to do with the fact that the file is auto-generated, I don’t know :man_shrugging:


#3

Some time ago List.indexOfFirst was specialized to iterate list by index rather than with the iterator(), but then we decided it’s not entirely correct given that we don’t know whether the list supports fast indexed access, so we reverted the implementation to be the same as for Iterable.


#4

Thank you!

So it means it is related to the historical implementation…

I just wonder since List implements Iterable and both classes’ implementations are the same, why not removing the implementation for List? (It does NOT mean I want to remove the api but I just want to know the reason why both implementation needed to exist, that’s all)


#5

I’m only like 99% sure of this so if this is wrong pls correct me :wink:


Let me try to explain.

interface Foo

class Bar : Foo

fun Foo.foo() ...
fun Bar.foo() ...

This code will be compiled to the following 2 functions in java:

static void foo(Foo receiver) {}
static void foo(Bar receiver) {}

If you now call those functions like this

val b = Bar()
b.foo()
val f = b as Foo
f.foo()

You get the following bytecode

...
INVOKESTATIC test1/Test1Kt.foo (Ltest1/Bar;)V // this calls  Bar.foo
...
INVOKESTATIC test1/Test1Kt.foo (Ltest1/Foo;)V // this calls Foo.foo

On the bytecode level it no longer knows that it those functions are “related”. It only knows call exactly this static function. If you now remove the version for List you can still compile, but you can no longer execute old programs without recompiling them as they still expect the function to be part of List and not part of Iterable.


#6

This are inline functions, so there is no call to the function in the compiled code. The code is copied there.


#7

Not always. The function is not marked with the internal annotation @InlineOnly, which means it is possible to call this function without inlining it. It could for example be called from java code which would not inline.