Can instance of functional interface as last parameter be reduced to {}

I really like the way a lambda as the last argument to a function can be represented as curly braces.
I also really like the way an instance of a functional interface can be defined just by a lambda block.

If the last argument to a function is an instance of a functional interface would it be possible to represent that as a ‘lambda block’.

Its not an essential feature, but it would be consistent, and it would make some cases much clearer.

public fun interface StringRequestor {
    public fun request(key: String): String
}

public class Model(
   private val requestor: StringRequestor
)

val model = Model { key ->
    key.reversed()
}
1 Like

It compiles!

public fun interface StringRequestor {
    public fun request(key: String): String
}

public class Model(
  val requestor: StringRequestor
)

val model = Model { key ->
    key.reversed()
}
fun main() {
  println(model.requestor.request("oof"))
}
5 Likes

Hm, my real attempt didn’t. I assumed that was because the syntax was not allowed. I never tried the example code that I wrote. Now I am

  • Pleased because I can do what I wanted
  • embarassed because I didn’t try my example code
  • mollified that I wrote valid code (that is a testament to the ease of using Kotlin).

Thanks kyay19.

Just tried the real code example again - it works! I should have more faith. Did that use-case just happen to work through good compiler design/code? If so I am well impressed.

4 Likes

Yes, I believe so. It happened to me for the first time in Python, when I realized that if a language was designed nicely, its features just “fit” to each other and can be used together to provide even more powerful features. x, y = y, x is a valid code, it is simple and it does exactly what we need, but Python doesn’t really provide a feature of swapping variables. It is a mix of 2 other features it provides.

The same happened to me in Kotlin so many times I lost the count. Probably my favorite example are “context scopes”. If we do:

val seq = sequence {
    delay(1.seconds)
    yield(5)
}

The code is obviously clear, we create some kind of a context block, with additional features for creating a sequence. Some languages even provide a specific feature for creating such context blocks, for example Python provides context managers.

But interestingly, Kotlin doesn’t provide such a feature. There are no context managers or scopes in Kotlin. We can do this “accidentally”, as a result of multiple other features:

  • calling a function.
  • lambdas
  • possibility to move the last lambda outside of parentheses
  • possibility to entirely skip parentheses
  • extensions and context receivers
  • inlining (not necessary, but good to not be “afraid” that we decrease the performance)

It may be sometimes confusing for beginners. it is not that easy to explain how DSLs work in Kotlin. But if you spend some time with it, it becomes perfectly clear and beautiful.

3 Likes

Exactly this. When the design is right and the coding is disciplined, things like this can just ‘happen’. It is always pleasing, and reassuring. But maybe something that only experienced designers/coders fully appreciate.

2 Likes

Link to documentation of SAM interfaces:

I suggest to read the whole official documentation at least one time.