Problem with mutual recursive local functions


#1

I’ve got a problem with mutual recursive local functions.
A local function cannot be called from another local function before it’s defined.

The following minimal example code doesn’t compile:

fun main(args: Array<String>) {
    fun f1() = f2()  // <-  Unresolved reference: f2
    fun f2() = 1
    println("${f1()}")
}

Information:Kotlin: Kotlin Compiler version 1.1.1
Error:(2, 13) Kotlin: Unresolved reference: f2

Of course here I could simply define f2() before f1() but if f1() needs to call f2() and f2() calls f1() this is not possible.

If I define f1/f2 outside of foo() it compiles without errors:

fun f1() = f2()
fun f2() = 1
fun main(args: Array<String>) {
    println("${f1()}")
}

Is there a possibility to forward declare a local function or is this a compiler limitation?


#2

Why do you insist on not using classes/objects with methods?
Correct me if I’m wrong but local functions are compiled to “lambda” class instances anyway.
Your code looks like very js way to declare private methods.

private fun f3() = f2()
private fun f1() = 1
private fun f2() = f1()
fun main(args: Array<String>) {
    println("${f3()}")
}

looks ok kotlin to me


#3

Thanks for your reply. I aware that member functions of a class don’t have this problem and can call each other.
It’s more a “visibility” thing to me. I just like to declare little helper functions which are of no use in a more global scope inside an other one. Just like local variables in a function or a block.


#4

They also have the advantage that they can access the parameters of the method in the closure without having to pass them as parameters so it is not just a way to make private methods


#5

In js you can use any var in scope and everybody loves it :wink:
Kotlin implements local functions as local lambda vars pointing to static instance field with implementation.
Looks like it is not possible to take refs to locally declared fun’s. I can think only of 2 more options. Lambdas in place of local fun’s and combinators.

fun lambdas(): () -> Int {
    val privateLocal = 1
    var forward: (() -> Int)? = null

    val f1 = { forward!!.invoke() }
    val f2 = { privateLocal }

    forward = f2

    return f1
}

fun combinators(): () -> Int {
    val privateLocal = 1

    return (fun(f1: (() -> Int) -> Int, f2: () -> Int) = { f1(f2) })(
            { f2 -> f2() },
            { privateLocal }
    )
}

btw this sample decompiles to java gore - repeated final/non final invoke methods in same class


#6

This is a compiler limitation, and we have no plans to address it at this time.


#7

I have been reading Robert Martin’s book “Clean Code” recently, and am very impressed by his many great ideas and suggestions in writing clean and readable code. However, it is a shame that this limitation of local functions of Kotlin make it not possible to fully implement the “Step down” rule as suggested in the book for organizing functions. I wish I could write the code in Kotlin using local functions according to the “Step down” rule as in the following example:

fun makeBreakfast() {
    addEggs()
    cook()
    serveBreakfast()

    fun addEggs() {
        val eggs = getEggsFromFridge()
        for (egg in eggs) {
            putEggInFryingPan(egg)
        }

        fun getEggsFromFridge(): Array<Egg> {
            return fridge.getEggs()
        }

        fun putEggInFryingPan(egg: Egg) {
            fryingPan.add(egg)
        }
    }

    fun cook() {
        // ..
    }

    fun serveBreakfast() {
        // ..
    }
}

However, because Kotlin does not allow code in a function to see local declarations in advance, the declaration of the nested local functions have to placed in position before they are referenced. The following is a compromised version:

fun makeBreakfast() {

    fun addEggs() {
        fun getEggsFromFridge(): Array<Egg> {
            return fridge.getEggs()
        }

        fun putEggInFryingPan(egg: Egg) {
            fryingPan.add(egg)
        }

        val eggs = getEggsFromFridge()
        for (egg in eggs) {
            putEggInFryingPan(egg)
        }
    }

    fun cook() {
        // ..
    }

    fun serveBreakfast() {
        // ..
    }

    addEggs()
    cook()
    serveBreakfast()
}

This actually violates the concept as suggested by the “Step down” rule that code of high abstraction level should be placed before those of lower abstraction because when people are reading code, a top-down direction of reasoning works better than a reversed one.

I really wish this behaviour of local functions could be revised to make it similar to that of top-level and class level functions where forward referencing is allowed.

That said, Kotlin’s local function feature is already a big step toward better readable code than with Java.