As indicated by search in this channel `value clas...
# compose
d
As indicated by search in this channel
value class
-es seem to still have two issues wrt compose: 1. They do not transfer
@Immutable
annotation to the wrapped type (unless it's Int) 2. Marking them as
@Immutable
and passing them to the composable functions is still treated as though they are non-stable Is this correct? Are there any issues which could be tracked for this apart from this one? P. S. Why I care about this: I have a kotlin-only module and want to mark some
data class
as immutable, but I can't because
@Immutable
requires
androidx.compose.runtime
and this is aar which can't be used in non-aar gradle modules.
a
Not necessarily. Using a value class is the same as using the wrapped type from the compiler's perspective, so the stability depends on the wrapped type.
You can use compose multiplatform for non-Android modules.
e
note that the annotation alone isn't enough, you need the code using the annotation to be built with the compose compiler plugin also for the annotation to have an effect in consumers (afaik)
d
Using a value class is the same as using the wrapped type from the compiler's perspective, so the stability depends on the wrapped type.
Hmm, but according to the issue I've linked this seems not to work atm.
note that the annotation alone isn't enough
oh, that's news for me. Then it seems I won't get it to work even with Albert's suggestion to use jb-compose...
s
You can use jb-compose and also apply jb-compose compiler to that module to get this functionality afaik
d
jb-compose compiler would be applied if I apply this plugin, is this correct?
Copy code
id("org.jetbrains.compose") version "1.4.0"
nothing like
buildFeatures.compose
in this case (which is specific to AGP)?
s
Not 100% sure but probably yes. I'd do that and generate the stability report so that it tells you if it has inferred them correctly or not.
d
ok, thanks!
a
This issue was fixed in androidx.compose.compilercompiler1.4.0
d
Oh, that's great to hear! By the way, can you clarify this bit: if I have a gradle module
A
without compose compiler plugin applied, and another module
B
with compose compiler applied and
B
depends on `A`: will
B
correctly see
@Immutable
and
@Stable
annotations in module
A
and skip recompositions accordingly? Or having compiler plugin applied everywhere is required?
s
The plugin does the work of inferring the stability of classes. If however you’ve already marked them yourself as stable, then that’s done, it won’t try to infer that class again. Which is also why it’s important not to wrongly mark something as immutable/stable, since that means that you’ll get correctness bugs. All a lie, Albert corrects me further down. You need the compiler to be applied to the module which contains the classes you’re interested in. only having the annotation is not enough.
a
Only adding the annotation without applying compose compiler plugin to the module where the class is defined is not enough. You can easily check this yourself by using compiler reports.
s
Wait, so a module not having the compiler, which does have a class annotated with Immutable means that the annotation is ignored? I thought I remember myself testing this in the past, but I must’ve done something wrong then. I was completely wrong in that case 🤯 And this means that I need to apply the compiler plugin in so many more modules than I thought I should. Is this expected behavior? That sounds so odd to me, annotating the class appropriately should really be enough for a module to do to play well with compose instead of having to apply the entire compiler plugin along with it.
a
Yeah basically that. See also this.
s
Wow my mind is blown now, I really had the wrong impression about this. So this means that basically any class coming from any 3rd party library, even if they try to play well with compose and annotate their classes it wouldn’t be enough. Very interesting to know, thanks a lot for correcting me!
d
Hm, actually somehow it's working for us, my coworker have just checked. We have: • a module without compose compiler (double checked, adding composeble fun results in IR-related build error) • this compose-less module depends on a
compose.runtime
artifact which is used to have
@Immutable
• this module has an
interface MutableData
marked as
@Immutable
, it has a simple impl without equals even • In another module with compose compiler applied there's a
data class ViewState(val a: Int, val b: MutableData)
And then this code
Copy code
@Composable
fun Screen(state: ViewState) {
  Widget(state.b)
}

@Composable
fun Widget(b: MutableData) {
  Log.d("tag", "recmposition")
}
Then we change
state.a
(int val) without changing
b
and we observe recomposition logs only when @Immutable is not applied in the compose-less module. If annotation is there, no recompositions are happening. So it seems compiler plugin is not strictly required in the dependent module? Or did we check something else here?
s
I'd check by using the reports that can be output from the compiler about what is stable and what is not. That should be the most reliable way to confirm how it works. Just as Albert suggested before.
d
But actual logs are a good measure too, in fact we confirm that function isn't called, and this is a debug build. Or can it somehow become "worse" and start to be called in the release build? 🙂
t
This is working for me too (compiler version 1.4.6). I tried the same example as you @dimsuz and the compiler metric report I got was: Without
@Immutable
on `interface MutableData`:
Copy code
unstable class ViewState {
  stable val a: Int
  unstable val b: MutableData
  <runtime stability> = Unstable
}
With
@Immutable
on `interface MutableData`:
Copy code
stable class ViewState {
  stable val a: Int
  stable val b: MutableData
  <runtime stability> = Stable
}
So was something fixed since the time this convo happened? https://kotlinlang.slack.com/archives/CJLTWPH7S/p1648226885245379?thread_ts=1648206217.729339&amp;cid=CJLTWPH7S
a
Okay, according to this post, it seems that you only need compose compiler plugin if you don't manually mark the classes with the annotations. Not sure if it was fixed at some point or it's the behavior from the beginning. Sorry for the confusion.
🥳 1
s
Ah that’s very good news though. No need to apply the compiler only to get this feature. That sounds much more nice definitely
👍 1