简体   繁体   中英

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 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!

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)() }

Does this work?

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

To explain my thinking... you promised to return a fun that takes an input of type I , so my lambda (the returned fun) declares that input.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM