Kotlin.receivers to limit the use of a method

Matias Reparaz
etermax technology
Published in
2 min readAug 12, 2019

Suppose you are writing a client for some underlying technology (e.i. a database) and you want to add some behavior before or after a method call (like a template method pattern). So, you can have something like this:

class SomeService(private val impl: ServiceImpl) {
fun something(param: String): Int {
otherThing(param)
return impl.doSomething(param)
}

private fun otherThing(param: String): Unit = TODO()
}

interface ServiceImpl {
fun doSomething(param: String): Int
}

The problem is that the methods SomeService.something and ServiceImpl.doSomething have the same signature and the question “Why do I need to use the SomeService if I already had the ServiceImpl?” will be asked. You can try to document this architecture or evangelize your client’s users. However, is it possible to change the signature of the ServiceImpl.doSomething to communicate that it belongs to the SomeService?

Let’s try these two approaches:

Option 1:

class SomeService(private val impl: ServiceImpl2) {
fun something(param: String): Int {
otherThing(param)
return impl.doSomething(param)()
}

private fun otherThing(param: String): Unit = TODO()
}

interface ServiceImpl2 {
fun doSomething(param: String): SomeService.() -> Int
}

This way, the ServiceImpl2.doSomething returns a function with SomeService as the receiver, then only an instance of SomeService can execute it, the drawback is that in the method SomeService.something needs to add the () to execute the actual function.

Option 2:

class SomeService(private val impl: ServiceImpl3) {
fun something(param: String): Int {
otherThing(param)
return (impl.doSomething)(param)
}

private fun otherThing(param: String): Unit = TODO()
}

interface ServiceImpl3 {
val doSomething: SomeService3.(String) -> Int
}

In this case, the function is defined as a value with the parameters embedded in the resulting function. This has two drawbacks, the first one being that the parameters lose its names and make it harder to understand the signature form the ServiceImpl3 viewpoint, and the second being that, by the precedence of kotlin, it is necessary to wrap the call to the impl.doSomething value.

Conclusion

Both options have pros and cons and I’m not even sure if this signature is good at communicating the use of the client, not to mention the bytecode generated by that signature. But it’s something that the language allows me to do and I want to know your opinions about it.

--

--