Danilo Pianini
Danilo Pianini

Reputation: 1106

Kotlin extension function of a function with receiver

I'm playing with Kotlin's extension functions. I'd like to create an extension function for boolean-returning function with receivers which returns the complement function.

My goal is to be able to write:

val isEven: Int.() -> Boolean = { this % 2 == 0 }
val isOdd: Int.() -> Boolean = isEven.complement()

I do realize there are better, clearer ways for doing what I'm doing, I want to better understand the language itself here, so please don't tell me to write isOdd as { !isEven() } :)

I want something like:

fun <I> (I.() -> Boolean).complement(): I.() -> Boolean = TODO()

Now,

fun <I> (I.() -> Boolean).complement(): I.() -> Boolean = this

compiles correctly, so the syntax definitely makes sense here. The problem is that this is of type I.() -> Boolean, and I'd need to access the "inner receiver" of my function with receiver, something like this@this, to write something like:

fun <I> (I.() -> Boolean).complement(): I.() -> Boolean = { !(this@this).this() }

where this@this would be of type I. Is there any way of achieving this effect?

Also, I noticed that I do not know how to invoke a function with receiver, I tried to:

fun <I> (I.() -> Boolean).complement(innerthis: I): I.() -> Boolean = { !this(innerthis) }

I get an error: expression 'this' of type 'I' cannot be invoked as a function. The function 'invoke()' is not found.

This sounds wrong to me! this should have type I.() -> Boolean, not I! I can't wrap my head around this error message.

I thought that maybe I'm just using a wrong syntax, so I changed to:

fun <I> (I.() -> Boolean).complement(innerthis: I): I.() -> Boolean = { !innerthis.this() }

But I get the same error. This is very confusing to me, as I'd expected innerthis to by of type I, and this of type I.() -> Boolean. My expectation seems to be confirmed by the implementation with = this to be compiling flawlessly.

Can someone explain to me the error the compiler is raising?

Thanks!

Upvotes: 4

Views: 315

Answers (1)

Ilya
Ilya

Reputation: 23125

You can disambiguate the outer this by the function name:

fun <I> (I.() -> Boolean).complement(): I.() -> Boolean = { !this@complement(this) }

Here this@complement is the receiver of complement function, and plain this is the receiver of the lambda function literal. For simplicity, this@complement is called like a function with one argument, however it is possible to call it as an extension function as well using a bit more tricky syntax:

fun <I>  (I.() -> Boolean).complement(): I.() -> Boolean = { !this.(this@complement)() }

Upvotes: 3

Related Questions