Use Case: DSL
Kotlin is well-suited for crafting domain-specific languages (DSLs). You can use the “extension function on a receiver” trick to offer small DSLs to simplify the use of some reusable bit of code.
For example, Android developers are starting to use Jetpack Compose, and its Compose UI subset, for defining user interfaces. Compose UI uses little DSLs in several places to help reduce the amount of code needed for simple scenarios.
This code snippet sets up a vertically-scrolling list:
LazyColumn {
items(content.forecasts) {
WeatherRow(it)
}
}
The items()
call inside of LazyColumn()
provides the list of model objects that represent the data for the list — in this case, as list of weather forecasts for a particular location. The lambda expression passed to items()
gets called as needed to render each item in the list, based on the number of items, if the user scrolls, etc.
The trailing lambda parameter on LazyColumn()
is defined as:
content: LazyListScope.() -> Unit
LazyColumn()
, as part of its implementation, creates a LazyListScope
object and uses that for invoking the content
parameter (scope.apply(content)
). LazyListScope
defines the API for the DSL: the functions that will be available to that content
lambda:
interface LazyListScope {
fun item(key: Any? = null, content: @Composable LazyItemScope.() -> Unit)
fun items(
count: Int,
key: ((index: Int) -> Any)? = null,
itemContent: @Composable LazyItemScope.(index: Int) -> Unit
)
}
LazyColumn()
, in turn, will use the items()
supplied by that lambda as part of rendering the list.
This is a fairly common pattern in Compose UI: a function that you call takes a trailing lambda, with the receiver set to be something useful for that trailing lambda to define what it is supposed to do. In the case of functions like LazyColumn()
, LazyRow()
, ConstraintLayout()
, and NavHost()
, the receiver is set to some object that implements an interface that represents the DSL, so we can concisely tell Compose UI what it needs to do.
Prev Table of Contents Next
This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.