Status Update
Comments
nj...@google.com <nj...@google.com>
zo...@gmail.com <zo...@gmail.com> #2
"This annotation can be applied to Composable functions so that no group will be generated around the body of the function it annotates. This is not safe unless the body of the function and any functions that it calls only executes "read" operations on the passed in composer. This will result in slightly more efficient code.
A common use case for this are for functions that only need to be composable in order to read CompositionLocal values, but don't call any other composables."
**Caution: Use of this annotation means that the annotated declaration MUST comply with this contract, or else the resulting code's behavior will be undefined**
This feels relevant. You can clearly abuse the API, but not without a fair warning. Similar story could be applied here.
le...@google.com <le...@google.com> #3
We have discussed something like this before. We would like to allow the compiler plugin to be configured with a list of classes that it ought to consider stable. This would allow for developers to mark classes outside of their control as stable, as well as, for instance, auto-generated classes from a data layer, etc.
It was decided that this wasn't part of the necessary requirements to ship, but it is still relatively high on the list.
Basically, we agree, and it is coming, but work on it hasn't yet started and I can't give any definitive dates.
ey...@gmail.com <ey...@gmail.com> #4
What about a scenario where as a library author, I want one of my types to be considered stable/immutable, but I don't want to include a dependency on Compose, or need to have all Compose consumers of my library explicitly specify that the type should be stable/immutable?
lu...@gmail.com <lu...@gmail.com> #5
As an example, we use LocalDateTime classes nearly everywhere in our composables. As Compose does not treat them as Stable (not even the kotlinx.datetime ones for some reason), we have to manually mark all State classes containing any LocalDateTime properties as @Stable.
For composables that consume LocalDateTime directly, we create our own wrapper data class to pass around in parameters and then convert it back to LocalDateTime in order to work with it.
zo...@gmail.com <zo...@gmail.com> #6
Initially I wrote about the render function with an annotated @Immutable parameter. Overall Ive switched my view on this, it looks fine for a render function like the one above, but with the annotation being available for use anywhere, it will likely end up everywhere as well - and result in a bug or two that are really hard to uncover.
Instead, I think an approach like the one used in compose itself is probably better (declare the stable classes somewhere). That way you need to be explicit about it, can keep all that code in one place; and can assign stability to ANY class, regardless if you own it or not.
I didnt have time to wait for this, so Ive done two things to make it happen already:
1) Ive annotated the renders/screens with @Immutable. This helps a lot.
2) Ive created wrappers for my domain models, that are annotated with @Immutable as well. I didnt need to wrap everything, e.g. a "Duration" class might still be unstable, but I just make sure to pass the "Training" class to a composable, and read duration inside of it. Ive used the compiler metrics to verify, and this basically reduced recompositions from 3 to 1 everywhere. It is a bit cumbersome to deal with, as I now have two sets of models that I need to keep in sync.
Hope this helps! :)
Description
There are some scenarios where it might not be feasible to annotate our classes/fields however;
- We might not have access to them directly, e.g. if they reside in a library, etc.
- We might not want to introduce compose concepts into layers that are irrelevant to compose.
With all of that said, recompositions are usually fast - so one could argue that this wouldnt have that big of an impact. I would argue the opposite, especially when it comes to cases where there are a lot of composables depending on a variable, and even more so when the variable changes frequently.
A system like this already exists! "Primitive value types (such as Int, Float, etc), String and enum types are considered, a priori, stable." I would simply like to see it extended so that we can configure our own set of inferred stable/immutable types.
I have to acknowledge the fact that the added flexibility can be abused, and in turn break compose without any clear hints as to why. Therefore I think the easiest approach is to allow the @StableMarker annotations on composable function parameters directly, it would make it easy to detect; unfortunately also easy to abuse if one doesnt understand the implications though.
My use-case / reasonings:
1) I dont want to introduce composable terms into my (multiplatform) domain layer, similar story is true for the presentation layer where many times the states are rendered outside of compose as well (e.g. to update a notification).
2) My states are always immutable and rendered by a 'Renderer' (see code [1] below) yet it would be very painful to annotate all current render types (plus, introducing compose terminology in a non-compose module), and remember to annotate any added types in the future by myself, and other team members.
3) When investigating performance issues with compose, I eventually applied the annotations to my states and saw a huge performance boost.
[1]: Current renderer interface
interface Renderer<in Render> {
@Composable
fun Render.Content()
}
[2]: Updated renderer interface (with @Immutable render)
interface Renderer<in Render> {
@Composable
fun Render(render: @Immutable Render)
}