Kotlin: WTF is this code (trailing lambdas)
I'm trying to learn Kotlin so that I can use Jetpack Compose so that I can make an Android app. Following the Android codelab, I almost immediately hit a roadblock: wtf is this code doing:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
GreetingCardTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "lol",
modifier = Modifier.padding(innerPadding)
)
}
}
}
}
}
As a C++ developer (with a little Java knowledge), some of this is bewildering. Especially:
WTF? It looks like a function call but there's no parentheses. It looks like a scope but how is the code run? I was scratching my head: does this have something to do with the annotation (no, that's just metadata), or something to do with accessors (no, you need to spell out get() and set()), or something to do with anonymous objects (no, you need to spell out object). Then I took a look at the setContent method declaration:
public fun ComponentActivity.setContent(
parent: CompositionContext? = null,
content: @Composable () -> Unit
)
Ok that's interesting, the first parameter has a default so it's likely optional, and the last parameter looks like a lambda. Maybe that has something to do with it? (BTW Kotlin Unit == C++ void) So I go to the Kotlin docs and, lo and behold, there it is: trailing lambdas.
According to Kotlin convention, if the last parameter of a function is a function, then a lambda expression passed as the corresponding argument can be placed outside the parentheses [...] If the lambda is the only argument in that call, the parentheses can be omitted entirely
That means that setContent is taking a lambda. That lambda also calls a function that takes a lambda, and so on. The entire UI hierarchy is just a bunch of lambdas that something (?) eventually executes... And, despite looking like a type due to PascalCase, they are actually functions (which normally use camelCase). How this all works is a question for another day.
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
// ...
}
In C++, lambda parameters go before the curly branches. In Kotlin, lambda parameters go inside the curly brace. Here, the Scaffold() function wants a lambda that takes padding parameters.
EDIT: To their credit, the Android Developer website mentions this in one of their codelabs. However, getting to the right codelab that contains this information is (IMO) not intuitive.