themishkun
05/21/2019, 12:11 PM@Compose
annotation rather than something more common in Kotlin dsl’s Compose.foo()
extensions on some context object?
It seems to me that it could make this more extensible and user-friendly than annotations and compiler plugins
I’ve read about it somewhere that devs went out from traditional “build-view-tree -> diff-view-tree -> dispatch-changes” way to improve performance. Are there any studies on how much actual performance gained?romainguy
05/21/2019, 2:06 PMLeland Richardson [G]
05/21/2019, 3:51 PMContinuation
, but they didn’t do that either.themishkun
05/21/2019, 4:37 PMLeland Richardson [G]
05/21/2019, 5:07 PMobject
, or as methods of a class, don’t make sense anymore. It made us realize that what we wanted was similar to a receiver scope, but not semantically identical.
2. We still wanted to do some code generation around invocations for many reasons, and the threading of the composer parameter didn’t get rid of all of those reasons. If we still need to do code gen, then we need an indication that the function is “special”. The receiver isn’t enough, because we also needed to have some functions that were just actual normal functions on the Composer object that is passed around. It made it hard to build a world where we didn’t also need another annotation, even if we were passing the Composer around as a receiver parameter. Having both seemed strictly worse.
3. With an annotation, it becomes realistic for us to label everything on the Composer as ~private APIs. We can make changes in how we do things, optimizations, etc. without the fear that user-land code is using these APIs improperly, if even accidentally.
4. We have built Compose to be generic to the type of tree it is emitting. Doing this properly is still being worked on (and is the reason we have to hackily import composer
right now). By exposing the context parameter as a receiver parameter, we have to participate in the type system in all the ways that kotlin receivers already work… doing this in a way that was compile-time safe meant that we would have to add an open generic type parameter with constraints to a lot of components, which adds significant cognitive overhead to the definition of a lot of components. For all of the other components, the type parameter would be specified… but it would feel redundant to a lot of people and probably promote type aliases and things like that which start to make the component ecosystem feel bifurcated and broken.
5. Mentioned already by Romain, but this meant that if you wanted to have components that were defined as receiver scopes to something else (for instance, a UserProfile component could be defined as an extension to a User
data type, really cleaning up the implementation.
6. Using receivers, for some pretty nuanced technical reasons, would mean that we wouldn’t be able to unify “Effects” and “Composable Functions”, which is something we are exploring currently.
7. Something I might explain in a blog post in the future is the fact that we had played around with the notion of “Class Components”. (These still exist in the repo right now). We gradually realized that class components are mathematically equivalent to composable lambdas that are memoized. This equivalence doesn’t work anymore if you start to use Composer as a receiver. This point isn’t so important that it would decide it on its own, but having the design of something actually remove concepts from the vocabulary of Compose is a good sign that it’s something more general IMO.
I think there were a few more, but that’s what I have off the top of my head. It might be worth mentioning that I was a big proponent of the receiver scope approach for a while, and was very much arguing for them, but after doing dilligent research on the effects of going this way, I think @Composable
is the right way, especially if we can come up with a general purpose language spec for what that means, and turn it into a keyword similar to suspend
instead. (Not saying that will happen necessarily, but it is something we want to explore)themishkun
05/21/2019, 5:49 PMromainguy
05/21/2019, 5:54 PMromainguy
05/21/2019, 5:56 PMthemishkun
05/21/2019, 6:19 PMgalex
05/21/2019, 7:40 PMcompose fun someComponent {
}
Leland Richardson [G]
05/21/2019, 7:48 PMromainguy
05/21/2019, 8:46 PMelizarov
05/22/2019, 8:39 AMgalex
05/22/2019, 12:52 PMLeland Richardson [G]
05/22/2019, 3:59 PM@Composable () -> Unit
doesn’t parse, you have to write @Composable() () -> Unit
. It seems to me we might be able to disambiguate if the parens are followed by ->
. This would help the annotation’s ergonomics a bit 🙂Leland Richardson [G]
05/22/2019, 3:59 PMelizarov
05/22/2019, 5:25 PMsimon.vergauwen
07/09/2019, 11:19 AMinternal
override. There are more similar restriction which makes it much more user friendly and Kotlin idiomatic compared to Scala implicits.
Sorry to re-ignite this old thread 🙂Leland Richardson [G]
07/09/2019, 5:06 PMsimon.vergauwen
07/09/2019, 5:16 PM