Thinking a bit about the implications of having a ...
# compose
z
Thinking a bit about the implications of having a codebase that uses Compose with more than one type of
Applier
. Since composable functions carry no compile-time information about which “type of composition” they can be used in, it seems like it would be a real headache, in a large codebase, to try and keep functions for each different composition type separate. And the penalty for getting that wrong could only be, AFAIK, a runtime crash. It seems like it would be very useful for composable functions to effectively include the type of composition they can be used with in their actual type. This would both serve as documentation (“ah,
foo()
is a UI function, and
bar()
is a function for managing a 3D scene graph”), and for the compiler to actually do type checking. I’d be really curious to know what thoughts the Compose team has about this potential issue, and how to mitigate it.
E.g. One way to do this would be to add a
KClass
parameter on the
@Composable
annotation that the compose compiler plugin could validate, so that a function annotated as, e.g.,
@Composable(UiApplier::class)
could only call other composable functions which have the
UiApplier::class
annotation argument. Alias annotations could be defined well, so e.g. Compose’s UI framework could define something like
@Composable(UiApplier::class) annotation class UiComposable
.
I could also see this being too restrictive. You could write a composable function that is aware of two different types of composition, figures out at runtime which one it’s being used in, and only calls other composable functions that are compatible with that type. This seems scary compared to a more strongly-typed system, and I’m not sure the flexibility would be worth the danger, but could allow stuff like a single high-level UI design system written as a set of composable functions that can either delegate to Compose UI on Android devices, or an Applier that manages javascript DOM elements on browsers.
r
We had something similar at first but it we didn't like it. Not because of the restrictions but because of the added boilerplate. But you're right that it could be useful to have a way to differentiate them. I guess the compiler could technically do that to at least give you useful and easy to understand compilation errors (in the ide too)
z
The compiler could do this without adding anything to the current API?
I thought I remembered some of the early presentations mentioning something about composable functions being type safe for different types of compositions, but haven’t seen any other documentation, discussion, or code about it since.
l
yes, everything you’ve said is essentially correct and we are of the same mind. we have loose plans to add inferencing at compile time to handle this correctly, but we’ve put it off as it isn’t as big of a priority as other things since most people won’t have more than one applier type
👏 5
essentially, the shape of
emit
is made partially to make this inferencing a little bit easier
z
Awesome! I don’t see this being a concern for us either until Compose is actually stable (if it ends up being an issue for us at all), but started to imagine trying to migrate from one completely separate Compose-based UI system to another without compiler help, and started to panic a little.
s
one completely separate Compose-based UI system to another
Tbh, even one compose-based UI system is not ready yet 😄 I completely agree and even imagine accidentally messing up non view related (text or vector) composables in
UiApplier
based calls. I guess the most important thing here is to design the API of such components in a way that prevents that. On the other hand, you could imagine these things having a bit different direction with multiplatform, where Composable functions go through `expect`/`actual` substitution and end up with different
Applier
types in the end.
a
What types are you mixing and if there are custom types, are you marking them with ComposableTargetMarker?
f
A custom type. I didn’t know ComposableTargetMarker before. I think that’s the point. Thanks Albert!
I tried ComposableTargetMarker. Defined an annotation like this:
Copy code
@Retention(AnnotationRetention.BINARY)
@ComposableTargetMarker("V Composable")
@Target(
    AnnotationTarget.FILE,
    AnnotationTarget.FUNCTION,
    AnnotationTarget.PROPERTY_GETTER,
    AnnotationTarget.TYPE,
    AnnotationTarget.TYPE_PARAMETER,
)
annotation class VComposable
Then marked my custom composable functions which call
ComposeNode
directly with it. But build can still pass. I even tried out mixing only
UiComposable
functions with
VectorComposable
functions. Also, no compile error occurred… 🤔
l
cc @Chuck Jazdzewski [G]
c
Can you report a bug to https://goo.gle/compose-feedback with an example of what you tried?
z
It’s a warning, not an error, unless you’ve enabled “treat warnings as errors”
f
Oh, yes. There is a warning. I didn’t notice it before.👍🏻
Copy code
...
... Calling a V Composable composable function where a UI Composable composable was expected
...