Effective Android

Share this post

The color of Composable functions 🖍️

newsletter.jorgecastillo.dev

The color of Composable functions 🖍️

A separate category of functions

Jorge Castillo
Nov 20, 2022
1
Share
Share this post

The color of Composable functions 🖍️

newsletter.jorgecastillo.dev

Composable functions have different limitations and capabilities than standard functions. They have a different type, and model a very specific concern. This differentiation can be understood as a form of “function coloring”, since somehow they represent a separate category of functions.

Function coloring

“Function coloring” is a concept explained by Bob Nystrom from the Dart team at Google in a blockpost called “What color is your function?”, written in 2015. He explained how async and sync functions don’t compose well together, since you cannot call async functions from sync ones, unless you make the latter also async, or provide an awaiting mechanism that allows to call async functions and await for their result. This is why Promises and async/await were introduced by some libraries and languages. It was an attempt to bring composability back. Bob refers to these two function categories as two different “function colors”.

In Kotlin, suspend aims to solve the same problem. However, suspend functions are also colored, since we can only call suspend functions from other suspend functions. Composing programs with a mix of standard and suspend functions requires some ad-hoc integration mechanism (coroutine launch points). The integration is not transparent to the developer.

Overall, this limitation is expected. We are modeling two categories of functions that represent concepts of a very different nature. It’s like speaking two different languages. We have operations that are meant to calculate an immediate result (sync), and operations that unfold over time and eventually provide a result (async), which will likely take longer to complete.

Effective Android is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.

Coloring in Compose

In Jetpack Compose, the case of Composable functions is equivalent. We cannot call Composable functions from standard functions transparently. If we want to do that, an integration point is required (e.g: Composition.setContent). Composable functions have a completely different goal than standard functions. They are not designed to write program logics, but to describe changes for a node tree.

It might seem that I am tricking a bit here. One of the benefits of Composable functions is that you can declare UI using logics, actually. That means sometimes we need to call Composable functions from standard functions. For example:

@Composable
fun SpeakerList(speakers: List<Speaker>) {
  Column {
    speakers.forEach {
      Speaker(it)
    }
  } 
}

The Speaker Composable is called from the forEach lambda, and the compiler does not seem to complain. How is it possible to mix function colors this way then?

Inline

The reason is inline. Collection operators are declared as inline, so they inline their lambdas into their callers making it effectively as if there was no extra indirection. In the above example, the Speaker Composable call is inlined within the SpeakerList body, and that is allowed since both are Composable functions. By leveraging inline we can bypass the problem of function coloring to write the logic of our Composables. Our tree will be comprised of Composable functions only.

But, is coloring really a problem?

Well, it might be if we needed to combine both types of functions and jump from one to the other all the time. However, that is not the case either for suspend or @Composable. Both mechanisms require an integration point, and therefore we gain a completely colored call stack beyond that point (everything suspend, or Composable). This is actually an advantage, since it allows the compiler and runtime to treat “colored” functions differently, and enable more advanced language features that were not possible with standard functions.

Language features

In Kotlin, suspend allows to model async non-blocking programs in a very idiomatic and expressive manner. The language gains the ability to represent a very complex concept in an extremely simple way: adding a suspend modifier to our functions. On the other hand, @Composable makes standard functions become restartable, skippable, and reactive, which are capabilities that standard Kotlin functions do not have.


Learn more 📖

This post is part of the first chapter of the Jetpack Compose internals book. If you want to learn more about Composable functions, the Compose compiler, runtime, and Compose UI, that might be a purchase you’d want to consider.

Jetpack Compose internals 📖

👨‍🏫 Online training

You might want to consider attending the next edition of the highly exclusive “Jetpack Compose and internals” online training I’m giving in February. I have carefully crafted it so attendees can master the library from the Android development perspective, while learning about its internals in order to grow a correct and accurate mental model. I’ll be your teacher 🙌

This course will allow you to position yourself well in the Android development industry. Earlybird seats still available.

Enroll here

1
Share
Share this post

The color of Composable functions 🖍️

newsletter.jorgecastillo.dev
Comments
Top
New
Community

No posts

Ready for more?

© 2023 Jorge Castillo
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing