fengdai
08/31/2020, 3:02 AM@Stable ’s guarantees? > The result of equals will always return the same result for the same two instances.Zach Klippenstein (he/him) [MOD]
08/31/2020, 2:37 PM=== and == must effectively return the same results for any two @Stable objects.Adam Powell
08/31/2020, 3:50 PM@Stable, then compose can declare that .equals indicates one instance is as good as another and that they are equal in how they will change and notify compose of any future changes.Adam Powell
08/31/2020, 3:52 PM@Stable type a and b. If you call
val param = if (something) a else b
MyComposable(param)
then this behavior becomes relevant.Adam Powell
08/31/2020, 3:54 PMMyComposable is initially called with a, and the caller recomposes and it is called with a again, we compare a == a, and determine we can skip the call to MyComposable.Adam Powell
08/31/2020, 3:55 PMa internally changes, (e.g. a field backed by mutableStateOf changes) the bits inside MyComposable that read those state values will run again - it's an internal lambda capture of the parameters from the last time MyComposable ran without skipping.Adam Powell
08/31/2020, 3:55 PMMyComposable will run again with a as the captured parameter, without recomposing the caller.Adam Powell
08/31/2020, 3:56 PMa == b. The stable contract says one equal instance is as good as another and we can skip the call.Adam Powell
08/31/2020, 3:57 PMMyComposable(b), the internal restart capture still points to a. If b changes and a doesn't reflect those same changes over time, MyComposable won't recompose.Adam Powell
08/31/2020, 3:58 PMa == b their future mutation destinies must also be linked.Adam Powell
08/31/2020, 3:58 PM.equals for a @Stable mutable type.Adam Powell
08/31/2020, 4:00 PMAdam Powell
08/31/2020, 4:00 PMvar data class constructor properties is a bad idea - it makes that object really dangerous to use as a key in hash tables.fengdai
09/01/2020, 1:28 AMfengdai
09/01/2020, 1:56 AM@Stable. And I think a does not equal b in this case:
val initialValue = ""
val a = mutableStateOf(initialValue)
val b = mutableStateOf(initialValue)
Even they hold the same initial value but they can be mutated respectively later, which can make them not equal anymore. (Am I right?) So in which case can two instances of MutableState be equal?Adam Powell
09/01/2020, 2:01 AMSnapshotMutableState does not implement `.equals`: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MutableState.kt;l=288fengdai
09/01/2020, 7:21 AMandroidx.compose.material.Colors is annotated with @Stable . And it's mutable, also implements .equals . As you mentioned above, if two instances of Colors a == b then a will be treated as the same as b. But how can it comply with the contract when a is changed? Is there some underlying mechanism of Compose that will build a link between a and b ?fengdai
09/01/2020, 9:05 AMval darkColors = darkColors()
val lightColors = lightColors()
@Composable
fun UnstableColors() {
val a = lightColors
var b: Colors? by remember { mutableStateOf(null) }
var darkTheme by remember { mutableStateOf(false) }
Column {
MaterialTheme(
colors = if (darkTheme) darkColors else lightColors,
) {
if (b == null) {
b = MaterialTheme.colors
}
Surface {
Column {
Text(text = if (darkTheme) "darkTheme" else "lightTheme")
Text(text = "a hash: ${a.identityHashCode()}")
Text(text = "b hash: ${b.identityHashCode()}")
Text(text = "a ${if (a == b) "==" else "!="} b")
}
}
}
Surface {
Switch(checked = darkTheme, onCheckedChange = { darkTheme = it })
}
}
}fengdai
09/01/2020, 9:48 AMColors is designed this way for performance consideration. But will the break of contract cause unexpected behavior when it’s misused by us?Adam Powell
09/01/2020, 2:42 PMColors - thank you 🙂 I'll follow up with the team