1
votes

The function send() in the following example calls itself recursively:

internal inner class RouteSender(
        val features: List<Feature>,
        val exchange: GrpcUniExchange<Point, RouteSummary>
) {
    var result: AsyncResult<RouteSummary>? = null // Set in stub for recordRoute.

    fun send(numPoints: Int) {
        result?.let {
            // RPC completed or err'd before sending completed.
            // Sending further requests won't error, but they will be thrown away.
            return
        }

        val index = random.nextInt(features.size)
        val point = features[index].location
        println("Visiting point ${RouteGuideUtil.getLatitude(point)}, " +
                "${RouteGuideUtil.getLongitude(point)}")
        exchange.write(point)
        if (numPoints > 0) {
            vertx.setTimer(random.nextInt(1000) + 500L) { _ ->
                send(numPoints - 1)
            }
        } else {
            exchange.end()
        }
    }
}

It can be re-written so that the last operation performed is the recursive call to itself:

...
if (numPoints <= 0) {
                exchange.end()
            } else {
                vertx.setTimer(random.nextInt(1000) + 500L) { _ ->
                    send(numPoints - 1)
                }
            }
...

Yet, if I mark it as a tailrec function, I get a warning that the recursive call is not a tail call. This doesn't stop compilation of the successful running of the program. However, why is this not a tail call?

The documentation says:

To be eligible for the tailrec modifier, a function must call itself as the last operation it performs. You cannot use tail recursion when there is more code after the recursive call, and you cannot use it within try/catch/finally blocks.

This is not within a try/catch/finally block and there is no more code after the recursive call. What is it that means this code block is ineligible for tail recursion optimisation?

I'll take a stab at answering my own question, in that it has no return value. Based on this discussion, that's about all I can think of. Thoughts?

2

2 Answers

7
votes

Although your method appears to contain a call to itself, it's not actually a recursive method at all.

The call to send appears inside a closure. That means that it's not invoked immediately. It will only be invoked when the closure itself is invoked. In your case, that's done by a timer. It will take place outside of the current call stack, and probably even outside of the current thread.

In any case, the last call is the call to vertx.setTimer.

0
votes

Kotlin allows inline closure functions which it uses for a number of it's own library functions, such as forEach. The tailrec might work if it's called from an inline closure, after all return from an inline closure returns from the outer function.

However, as noted above, this is a timer callback function, so it can by definition not be an inline call, and not be tail recursive.