Iterator(), next() and hasNext(). Why are they operator fun?

https://kotlinlang.org/docs/reference/control-flow.html#for-loops

As mentioned before, for iterates through anything that provides an iterator, i.e.

  • has a member- or extension-function iterator() , whose return type
    • has a member- or extension-function next() , and
    • has a member- or extension-function hasNext() that returns Boolean .

All of these three functions need to be marked as operator .

abstract operator fun iterator(): Iterator<T>
abstract operator fun hasNext(): Boolean
abstract operator fun next(): T

I found that they must be operator fun and I tried to understand operators in Kotlin, but I can’t understand why. Is there any document how and when they’re interpreted as operator?
I don’t know, but maybe they’re treated as operator because they’re called without explicit function call statement, but it’s not easy to find about these things in documents.

Yes! The key point is that there is a special syntax that will make use of these functions without a syntactically visible call. Without marking them as operator, the special syntax would not be available.

2 Likes

Thanks for the explanation.
I’m not familiar with language design theory, so I don’t know if this is common philosophy of operators, but it would be better if the language document explicitly explains it because it explains operator overloadings and operator-thing in iterator which made me think it’s not explaining operator keyword fully.

There is only a small set of function names that can be marked as operators in Kotlin. Once you have learned them, that’s it. Repeating how they work in the documentation of every instance of an operator function would be superfluous.

1 Like

I understand. But I was curious about why they are operator funs (I mean how they are interpreted and works in common way), rather than listing all or each of them.

The reason why they need to be flagged as operator is probably just to make them more explicit. I don’t think there is a theoretical necessity to mark such functions as operators. Theoretically it could work as soon as the function name and signature matches the operator. In fact, afair methods defined in Java implicitly are treated as operators when their signatures match.

1 Like

I got it. I just thought there must be a rule about it, but maybe thinking about it was too much. Thanks for the opinion.

In addition, maybe it has two other meaning:

At first, I think marking with ‘operator’ can avoid the calling which is treated as operators coincidently but you do not want , when the name of function is just same as syntax, so it need to be flagged explicitly with ‘operator’

Secondly, you can declare the operator function with extention function , rather than all in the class body; At this
time you can know that it is operator explicityly.

Additionly, the result of function ‘iterator’ do not need bo be inhertor of interface ‘Iterator’, and it could work as soon as the result has two function which matches the operator . you can see the demo below:

    class A {

        private var i = 0

        operator fun hasNext() = i < 10

        operator fun next() = i++
    }

    operator fun A.iterator() = this


    fun foo(){

        for(i in A()) print(i)
    }
1 Like

In fact, It also used to work that way for Kotlin functions. That was changed back in 2015:

This has been debated a lot in the past, and we finally decided that we want to introduce some more discipline into how operator overloading and infix function calls work in Kotlin. At the moment any function named plus that can be called as a.plus(b) can also be called as a + b . We will require such functions to be marked with the operator modifier, otherwise the operator notation will not be available for them. This makes operator use more disciplined and eliminates the possibility of random punctuation creeping into APIs. The most common example would be having a method called get but totally not intending it for use as square brackets.

1 Like

I believe in Java, classes are eligible to for-each loops when they implement Iterable

I was talking about Java classes that are used in Kotlin code.

1 Like

In fact I don’t understand the interest to not explicitly implements Iterator and/or Iterable when you create your own classes. It more clear that the capacity to iterate is present without check all code to see if there those operator or not.

There also a draw back, only for(t in a) will work, can’t use as Iterator or Iterable, so loose some functionalities and extensions

The only interest that I can see is if use an external library and need add these behavior with extensions.

1 Like

You can also avoid boxing by not implementing Iterable/Iterator. IMO it’s nice that the type system isn’t needed to “talk to the compiler” like this.

object SquareNumbers {
    operator fun iterator() = SquareNumbersIterator()

    class SquareNumbersIterator {
        private var i = 0

        operator fun hasNext(): Boolean = true
        operator fun next(): Int = (i * i).also { i++ }
    }
}

for (n in SquareNumbers) {
    if (n > 100) break
    println(n)
}

This also reminds me of an interesting C# proposal about allowing using (i.e. the try-with-resources of C#) to work with types that have Dispose() functions without actually implementing IDisposable (which is what using requires). Basically wanting to treat Dispose() as a Kotlin operator function and decouple using from the type system.

1 Like