Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it possible to call a higher order function's extension function parameter as a normal function in Kotlin?

Let's consider this code:

class Receiver

fun higherOrder(extensionBlock: Receiver.() -> Unit) {
    Receiver().extensionBlock() // works
    extensionBlock(Receiver()) // works
}

fun Receiver.extension() = Unit
fun f() {
    Receiver().extension() // works
    extension(Receiver()) // unresolved reference
}

In my mind extension's functional signature is Receiver.() -> Unit, same as extensionBlock parameter's; i.e. we can call higherOrder(Receiver::extension) fine. If this is true, I don't understand why the normal function call syntax is not consistent between an extension function and a parameter of the same type. I'm actually happy that extension(Receiver()) is not allowed, so there's only one way to call extension functions. So I guess the main question is Why can I do extensionBlock(Receiver()) when extensionBlock has an extension function signature?

like image 254
TWiStErRob Avatar asked Nov 28 '19 12:11

TWiStErRob


People also ask

Why does Kotlin have higher order functions?

The higher order function returns the result of operation invocation with the supplied arguments. Declares a function that matches the operation signature. Invokes the higher-order function passing in two integer values and the function argument ::sum . :: is the notation that references a function by name in Kotlin.

Why are higher order functions called higher-order?

A function that accepts and/or returns another function is called a higher-order function. It's higher-order because instead of strings, numbers, or booleans, it goes higher to operate on functions.

How do I call higher-order function Kotlin?

In Kotlin, a function which can accept a function as parameter or can return a function is called Higher-Order function. Instead of Integer, String or Array as a parameter to function, we will pass anonymous function or lambdas. Frequently, lambdas are passed as parameter in Kotlin functions for the convenience.

What makes a function a higher-order function?

A higher order function is a function that takes a function as an argument, or returns a function . Higher order function is in contrast to first order functions, which don't take a function as an argument or return a function as output.

What are higher-order functions in Kotlin?

Higher-order functions in Kotlin are the functions that return a function or take a function as an argument or both. In other words, in Kotlin, you can have a function that takes another function as a parameter, return a function as a result, or does both, and these functions are called higher-order functions.

What is functional programming in Kotlin?

Kotlin language has superb support for functional programming. Kotlin functions can be stored in variables and data structures, passed as arguments to and returned from other higher-order functions. In Kotlin, a function which can accept a function as parameter or can return a function is called Higher-Order function.

How to filter a list of strings in Kotlin using extension functions?

This is a simple extension function that allows a List of Strings to be filtered Run the code in the Kotlin playground. Here’s the sophisticated version of this leveraging Extension Function Expressions! Run the code in the Kotlin playground. allow: T. () -> Boolean is used instead of allow: (T) -> Boolean (from the unsophisticated example).

How do you return from a lambda function in Kotlin?

When the return executes in the lambda, it returns from the function in which the lambda was called from (not just the lambda block itself). The return from the outer function is possible only if the function that takes the lambda as an argument is inlined. More information on when to inline extension functions in Kotlin in Action, Ch 8 .


2 Answers

Maybe looking at the byte code helps...

extensionBlock : Receiver.() -> Unit is a Function1<Receiver, Unit>. Under the hood both calls are basically similar to extensionBlock.invoke(Receiver())... (you could have even written that one as well in your comparison...) ... so the compiler does the magic that both work... why isn't it doing that also in the extension variant?

Extension functions are just static functions that take the receiver as argument. Now a guess why it isn't supported the same way as with higher order functions, even though technically it could be possible. Having something as follows in place, should it be an extension function then too?

fun extension(r : Receiver) = Unit // to extend or not to...?

I think it should, if you want to support extension(Receiver()) for extension functions (and from the byte code there is no difference). For now you couldn't have both functions in place, as you would get a platform declaration clash then (which basically speaks in favor to why extension(Receiver()) should work with the extension function).

I wonder whether it would be more or less logic for the compiler to handle if it were this way... to be honest, I like that higher order functions and extension functions differ in that regard... For me an extension function or a function taking something as argument are two different sort of things... it would probably be more confusing if an extension function could be used in both ways, i.e. extension(Receiver()) and Receiver().extension()... probably it would be more confusing if ~normal functions would also be available as extension functions...

A pity, but also logical, that this on the Java side works: <WrapperClass>.extension(new Receiver()) (but we have no real extension there, so it's ok ;-))

like image 176
Roland Avatar answered Oct 16 '22 04:10

Roland


These examples are two different things: First one is a function type with receiver A.() -> B, second one is an extension function.

1. Fuction with receiver:

TL;DR: A.() -> B is different way to write (A) -> B. It does not extend anything.

Non-literal values of function types with and without receiver are interchangeable, so that the receiver can stand in for the first parameter, and vice versa. For instance, a value of type (A, B) -> C can be passed or assigned where a A.(B) -> C is expected and the other way around:

val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
val twoParameters: (String, Int) -> String = repeatFun // OK

Kontlin doc

In your case, the code applied to the explanation would look like:

val typeFunWithReceiver = Receiver.() -> Unit
val typeFunWithParam : (Receiver) -> Unit = typeFunWithReceiver // It does NOT extend Receiver

It is indeed confusing also to me now you're mentioning it. Even in the kotlin doc they refer to the resemblance:

This behavior is similar to extension functions, which also allow you to access the members of the receiver object inside the body of the function.

Source

2. Extension function

TL;DR: A.funName() -> B extends the A class by the function funName(). It is not a method type like before.

Kotlin provides the ability to extend a class with new functionality without having to inherit from the class or use design patterns such as Decorator.

Source

like image 30
Neo Avatar answered Oct 16 '22 03:10

Neo