More concise nested lambdas

Suppose I have a code that looks like this:

thing.doSomething {
    doSomethingElse {
        ...
    }
}

This is specially common on Android, with so many addOnSomethingListener being called from everywhere. Is there a way to make nested lambdas like this more concise? Like so:

thing.doSomething -> doSomethingElse {
    ....
}

// OR

thing.doSomething, doSomethingElse {
    ....
}
2 Likes

Do you often make literally something like this?

thing.doSomething {
    doSomethingElse {
        ...
    }
}

Or maybe this is more like this:

thing.doSomething {
    myService.doSomethingElse(someParam) {
        ...
    }
}

Or:

thing.doSomething {
    // some code    

    doSomethingElse {
        ...
    }

    // some code
}

I ask because I think both latter cases are much more realistic and I’m not sure how this shortened syntax could work with such cases.

Also I’m not sure if this:

thing.doSomething -> doSomethingElse {
    ....
}

Is really much more concise than this:

thing.doSomething { doSomethingElse {
    ....
}}
2 Likes

The issue here I think is with receivers and parameters. The trouble is that currently in Kotlin you can’t just simply compose 2 functions that take lambdas together like that because what do you do for parameters? You’d have to define way to many functions to define those lambdas in a typed fashion.

Like broot said, I think maybe this style should be considered idiomatic for conciseness sake:

thing.doSomething { doSomethingElse {
    ....
}}

i don’t see any reason why you couldn’t simply make a context class and use an extension function for something like this, or perhaps even just an extension function.

inline fun <T> T.doSomethingElseInThing(block: SomethingElseScope.() -> Unit) = doSomething {
    somethingElse(block)
}

i’m not sure your exact use case here but there could be a way of generalizing this so you only need to write it once or twice and use it wherever needed

thanks for the awesome information.

1 Like

Also note that if you like hacking and weird solutions that make others curse you for introducing them, then you can do something like this:

fun main() {
    ::doSomething then ::doSomethingElse then ::doSomethingMuchElse then2 {
        println("end!")
    }
}

fun doSomething(onSuccess: () -> Unit) {
    println("doSomething()")
    onSuccess()
}

fun doSomethingElse(onSuccess: () -> Unit) {
    println("doSomethingElse()")
    onSuccess()
}

fun doSomethingMuchElse(onSuccess: () -> Unit) {
    println("doSomethingMuchElse()")
    onSuccess()
}

infix fun ((() -> Unit) -> Unit).then(next: (() -> Unit) -> Unit): (() -> Unit) -> Unit {
    return { next2 ->
        invoke {
            next(next2)
        }
    }
}

infix fun ((() -> Unit) -> Unit).then2(next: () -> Unit) {
    invoke(next)
}

Output:

doSomething()
doSomethingElse()
doSomethingMuchElse()
end!

You can rename then2 to something more meaningful and provide alternative “thens” for passing results between functions, so e.g. result of doSomething() is passed as a parameter to doSomethingElse(). Still, I don’t consider this a good idea :slight_smile:

1 Like

Why not

thing.doSomething(doSomethingElse {
    ....
})

In this case that would pass the result of doSomethingElse to doSomething, which is completely different

Omg I actually am that guy and I actually implemented such ‘then’ fun. Even worse, it had three generic parameters because it allowed chaining mapping operations.

And then I had to hand-over the codebase to clueless JAVA devs who asked questions like “what are unit tests” or “how do I switch branch” (company decided to cut costs by hiring twice as many devs which were 7 times cheaper each). The best day in my career XD.

3 Likes

thanks my issue has been fixed.