Reputation: 1106
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
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