Hey, Can someone please help me in why `Bar` is no...
# compose
a
Hey, Can someone please help me in why
Bar
is not recomposing with the new lambda instance? In this example code, I am creating a new instance of
onBarClick
every time the state changes. Since it's a new instance, the
Bar
should recompose as well — but it's not. I've disabled strong skipping using
enableStrongSkippingMode = false
.
Copy code
@Composable
fun Foo() {
    val state = remember {
        mutableIntStateOf(1)
    }

    val onBarClick = {}

    Log.d("logs", "Foo Recomposition ${onBarClick}" )

    Column {
        Text(text = "Value ${state.value}")
        
        Bar(onBarClick = onBarClick)

        Button(onClick = { state.value++ }) {
            Text(text = "Increment")
        }
    }
}

@Composable
fun Bar(onBarClick: () -> Unit) {
    Log.d("logs", "Bar Recomposition" )
    Button(onClick = onBarClick) {
        Text(text = "Dummy Button")
    }
}
Thanks
e
kotlin compiler optimization - if the lambda doesn't capture anything, it can be made into a singleton
https://pl.kotl.in/Eka0bqvUz in non-compose code
a
I have already checked that possibility, it is not converting into a singleton.
Screenshot 2025-05-05 at 6.42.24 PM.png
w
I'm not sure this is Kotlin compiler, unless you're counting Compose compiler. It's probably just that Compose doesn't see any reason to recompose Bar? It might be inferring that the parameter never changes. You can probably see it skipping if you debug the recompositions on an Android device.
s
This lambda should be memoized by Compose, I am surprised you are getting new instances each time in the logs though
a
@Winson Chiu Yes, it is skipping. But why it is skipping that's the question
w
No, like, if you debug the function I think you can see why it skipped. I can't remember the specifics of where the breakpoint needs to be.
It's expected though, because it would remember the lambda and Compose would consider them equal.
I wonder if you can disable lambda memoization entirely with some flag. Just disabling strong skipping isn't enough I don't think.
Maybe if you move the lambda allocation to a non-composable method, the compiler won't do any trickery. But you'd have to make sure Kotlin itself actually creates a new one each time.
a
I not sure about the debugging. But 1. I have checked that old lambda instance is not equal to new instance. 2.
val onBarClick =  @DontMemoize {}
with this option I am getting same instance, which is probably because of kotlin compiler optimization - if the lambda doesn't capture anything, it can be made into a singleton
w
If you generate a compiler stability report, is the Bar method marked as always stable or something? Might give you some hint of why it's not being called.
a
this is from the compiler report:
Copy code
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun Foo()
restartable skippable scheme("[androidx.compose.ui.UiComposable]") fun Bar(
  stable onBarClick: Function0<Unit>
)
e
IIRC lambda (either kotlin or compose compiler generated) stability is based on its captures, and where it does not capture anything, both instances would be equal, possibly explainng why its being skipped