In the following code the parameter type for modelInitializer is
CalendarMonthTitleModelBuilder.()
What does the .()
mean?. I believe the dot refers to an extension. And when you add ()
after it, I think it means to create an instance of this type. Does this mean some anonymous extension is being created here and initialized?
inline fun EpoxyController.calendarMonthTitle(modelInitializer: CalendarMonthTitleModelBuilder.() ->
Unit) {
CalendarMonthTitleModel_().apply {
modelInitializer()
}
.addTo(this)
}
What happens if you leave out the dot before ()
?
The correct name for this is called lambda with receiver
You've started in the right direction. So an easy way to think of this is by starting in extension functions:
fun CalendarMonthTitleModelBuilder.foo() = //...
The function foo
is an extension function on the type CalendarMonthTitleModelBuilder
.
Let's approach it from another angle. Let's talk about higher-order functions, a.k.a. functions that take other functions as parameters:
fun higherOrder(func: () -> Unit) = //...
This function receives a lambda
that receives no parameters and returns Unit
. What could one do if we wanted to use a CalendarMonthTitleModelBuilder
inside the lambda? An easy way is to pass it in:
fun higherOrder(func: (CalendarMonthTitleModelBuilder) -> Unit) = //...
Calling this function, would be something like this:
higherOrder {
it.someMethod()
}
(here someMethod is part of CalendarMonthTitleModelBuilder)
However, we can somehow make this lambda an extension to CalendarMonthTitleModelBuilder
by using a similar sytax to the extension functions:
fun higherOrder(func: CalendarMonthTitleModelBuilder.() -> Unit) = //...
The difference now, is that we've created a lambda with receiver, meaning instead of using the implicit parameter it
, we can use this
, or better yet, omit it:
higherOrder {
someMethod()
}
inside the lambda, this
is an instance of CalendarMonthTitleModelBuilder
, so you can simply call someMethod
.
These constructs are often used in DSL's and you see them a lot in examples like yours - with the builder pattern.
Here's a very simple example. Let's assume you have UserBuilder
class that builds users and you want to create a small DSL for this (this is an exaggeration of the pattern, but suits to help out I think):
data class User(
val email: String,
val password: String)
class UserBuilder {
var email: String = ""
var password: String = ""
fun build() = User(email, password)
}
One can begin by writing a higher-order function like so:
fun user(func: UserBuilder.() -> Unit) =
UserBuilder().apply(func)
Inside the method, we create an instance of the builder and apply to it the lambda. This is a simple trick so we can keep on chaining the methods and at the end call build
. For example:
user {
email = "foo@bar.com"
password = "123456"
}.build()
It's possible to go even further using extension functions:
fun UserBuilder.withEmail(emailBuilder: () -> String) {
email = emailBuilder()
}
Which let's you do:
user {
withEmail {
"foo@bar.com"
}
}.build()
we can call withEmail
inside user
because withEmail
is an extension function on UserBuilder
and inside user
this
is of type UserBuilder
due to the lambda with receiver.
You can do something similar to the password.
First of all ()->Unit
is a lambda. And this lambda has a this
, and it's type is the CalendarMonthTitleModelBuilder
. It's called lambda with a receiver.
So inside modelInitializer
you can access to CalendarMonthTitleModelBuilder
as you are inside this class: using this
.
For example, I assume that CalendarMonthTitleModelBuilder
has a someMethod
:
val epoxyController: EpoxyController = EpoxyController()
epoxyController.calendarMonthTitle {
// inside these curlies `this: CalendarMonthTitleModelBuilder = CalendarMonthTitleModel_()`
this.someMethod() //you can use 'this' inside this lambda
someMethod() // or as usual just skip writing 'this', just access `CalendarMonthTitleModelBuilder` methods as you inside it's class
}
This feature is essential in building DSLs.
Here is a link to documentation.