Understanding a lambda construct that contains dot followed by brackets

Viewed 123

This is the function declaration for rememberCoilPainter:

@Composable
fun rememberCoilPainter(
    request: Any?,
    imageLoader: ImageLoader = CoilPainterDefaults.defaultImageLoader(),
    shouldRefetchOnSizeChange: ShouldRefetchOnSizeChange = ShouldRefetchOnSizeChange { _, _ -> false },
    requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null,
    fadeIn: Boolean = false,
    fadeInDurationMs: Int = LoadPainterDefaults.FadeInTransitionDuration,
    @DrawableRes previewPlaceholder: Int = 0,
): LoadPainter<Any> {

}

The line of code I am having difficulty understanding is:

requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)? = null

A dot appears after Builder followed by (size: IntSize)

This is the first time I've seen this construct in Kotlin and am not sure how to interpret it. This is a lambda. Normally the dot after an object refers to a sub component of a class or a package. But the ( ) after the dot isn't clear.

How do I implement the requestBuilder parameter?

3 Answers

This is a function with receiver type as described here: https://kotlinlang.org/docs/lambdas.html#function-types

Function types can optionally have an additional receiver type, which is specified before a dot in the notation: the type A.(B) -> C represents functions that can be called on a receiver object of A with a parameter of B and return a value of C. Function literals with receiver are often used along with these types.

It could be tricky to understand at first, but this is like you are providing a function/lambda that is a method of ImageRequest.Builder. Or in other words: your lambda receives one additional parameter of type ImageRequest.Builder and it is available in the lambda as this.

You can provide requestBuilder as any other lambda, but note that inside it you will have access to properties and methods of ImageRequest.Builder object that was provided to you.

What you are looking at is a "function literal with receiver". Speaking generically, a type A.(B) -> C represents a function that can be called on a receiver object of A with a parameter of B and return a value of C. Or in your example:

requestBuilder: (ImageRequest.Builder.(size: IntSize) -> ImageRequest.Builder)?

We have a function requestBuilder which can be called on a ImageRequest.Builder with a parameter size: IntSize and returns another ImageRequest.Builder.

Calling this function is just like calling any other function with a lambda as a parameter. The difference: You have access to ImageRequest.Builder as this inside your lambda block.

Hope the following example helps understand lambdas with receiver type:

data class Person(val name: String)

fun getPrefixSafely(
    prefixLength: Int,
    person: Person?,
    getPrefix: Person.(Int) -> String): String
{
    if (person?.name?.length ?: 0 < prefixLength) return ""
    return person?.getPrefix(prefixLength).orEmpty()
}

// Here is how getPrefixSafely can be called
getPrefixSafely(
    prefixLength = 2,
    person = Person("name"),
    getPrefix = { x -> this.name.take(x) }
)

How do I implement the requestBuilder parameter?

Hope this part of the code snippet answers the above:
getPrefix = { x -> this.name.take(x) }

PS: These lambdas with receiver types are similar to extension functions IMO.

Related