Compiler behavior question - this crashes: ```clas...
# compose
n
Compiler behavior question - this crashes:
Copy code
class Obj(val id: Int) {
    @Composable fun id() = remember { id }
}

// later ...
var obj by remember { mutableStateOf(Obj(0)) }
LaunchedEffect(obj) {
    delay(1000)
    obj = Obj(obj.id + 1)
}
require(obj.id == obj.id())
I understand what's happening and I know how to solve it (use
remember(this)
in Obj, or
key
) but I wonder if this is how it should be. Wouldn't it be desirable that the compiler recognizes
obj
as part of the remember call stack? Since
id()
is a member function, it's pretty clear that if the object changes, the old remember should be invalidated.
z
it’s pretty clear that if the object changes, the old remember should be invalidated.
It might be for your use case, but that’s not necessarily true of all cases. It’s very possible that a class’s composable method might want to retain state across different receivers.
I’m not sure i understand what you’re trying to do though
n
It's not really important, I was just questioning the default behavior. My example makes the root cause very explicit, but imagine you're just writing the Obj class without knowing how it will be used, no knowledge of the compose graph - I bet most people would not think that your compose code could be shared with other Obj instances.
One could argue that composing with member functions breaks the stateless functional programming paradigm, but it's not rare to see this. Even compose runtime does it. I feel like it would be more intuitive to automatically consider the instance in a OOP context (= member functions)
z
imagine you’re just writing the Obj class without knowing how it will be used
Well that’s almost always going to be a disaster. Put another way, if someone really wants to misuse an API from how it was designed, they’re gonna have a bad time.
One could argue that composing with member functions breaks the stateless functional programming paradigm
How so? The method’s receiver is effectively just another argument to the function, with slightly different syntactic rules but from a data flow perspective it’s just another argument.
n
Just because objects hold state. Function arguments can hold state as well, but in that case we intuitively do remember(arg). It's not intuitive to do remember(this). Seems that we agree that it can easily be a disaster, is error prone, not intuitive, so I'm not sure why it has to be like this. Can you make an example of Obj.id() or similar member fun where you use remember {} instead of remember(this) {} explicitly thinking "I need this to be shared with other Obj instances, if they happen to have the same call stack of this function"?
z
I don't understand what that
id()
method is for. Why not just read the
id
property directly? And I don't really understand the rest of that code either, it could just be this:
Copy code
val id by remember { mutableStateOf(0) }
LaunchedEffect(Unit) {
  delay(1000)
  id++
}