Skip to content

Support Kotlin higher-order functions/lambdas #977

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
jsommr opened this issue Mar 19, 2018 · 4 comments
Open

Support Kotlin higher-order functions/lambdas #977

jsommr opened this issue Mar 19, 2018 · 4 comments

Comments

@jsommr
Copy link

jsommr commented Mar 19, 2018

NativeScript supports marshalling Java bytecode generated by Kotlin. But Kotlin has a way to specify that a function takes another function in a much better way than Java.

Like this:

fun myfun(callback: (arg: String) -> Void)

It compiles to something like

public final void myfun(@org.jetbrains.annotations.NotNull final kotlin.jvm.functions.Function1<? super String, Void> callback)

where Function1 is a class containing an invoke method. Depending on how many args the function takes, it'll be called Function2, Function3 and so on.

Interacting with myfun from JavaScript would be done this way:

myfun(new kotlin.jvm.functions.Function1({ invoke: () => console.log("invoked") }))

It would be so cool if NativeScript supported Kotlin-functions out of the box, so the callback wouldn't need to be wrapped in FunctionX and myfun could be run via myfun(() => console.log("invoked")). This would make it easier to align with an Objective-C library, where blocks are supported.

To support Kotlin, my app.gradle currently looks like this:

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.3.3'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.2.30"
    }
}

dependencies {
  compile fileTree(dir: "libs", include: ["*.jar"])
  compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.30"
}

android {  
  defaultConfig {  
    generatedDensities = []
    applicationId = "org.nativescript.test"  
  }  
  aaptOptions {  
    additionalParameters "--no-version-vectors"  
  }  
} 
@jsommr jsommr changed the title Support Kotlin functions Support Kotlin lambdas Mar 19, 2018
@jsommr jsommr changed the title Support Kotlin lambdas Support Kotlin higher-order functions/lambdas Mar 19, 2018
@jsommr
Copy link
Author

jsommr commented Mar 19, 2018

If someone can give me a few hints about where to look, perhaps I can make the appropriate changes myself :)

@petekanev
Copy link
Contributor

@nerfpops I don't find this feature to have a straightforward implementation. Here's why:

You'd have to generate additional metadata for the generated anonymous functions. Next step is figuring out how to wire the lambda callback to the equivalent of a fat arrow function declaration, and fat arrow function declaration to a kotlin lambda. Are there cases where javascript fat arrow functions should not be treated as kotlin lambdas? Should named functions be allowed as parameters? Will there be a problem if the functor (myFunc) expects a callback with a number of parameters, but those are not handled in the javascript function? Is it possible that the functor has many overloads of different callback signatures?

@jsommr
Copy link
Author

jsommr commented Mar 19, 2018

@Pip3r4o Not a Kotlin-expert in any way but I'll try to answer your questions

Are there cases where javascript fat arrow functions should not be treated as kotlin lambdas?
According to the documentation lambda expressions supports the following syntax:

class HTML {
    fun body() { ... }
}

fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()  // create the receiver object
    html.init()        // pass the receiver object to the lambda
    return html
}

// Example usage
html {       // lambda with receiver begins here
    body()   // calling a method on the receiver object
}

Using html from javascript the same way as the example above, would look like

html(function() {
    // `this` is now the class returned from html
    this.body()
})

// or

html(context => {
    context.body()
})

I'm sure this wouldn't be easy to implement. Could it be something that's unsupported in the start?

Are there cases where javascript fat arrow functions should not be treated as kotlin lambdas?
The only method on FunctionX is invoke. (arg1, arg2, ...) => { code } would simply be a shorthand for new kotlin.jvm.functions.FunctionX({ invoke: (arg1, arg2, ...) => { code } })

Should named functions be allowed as parameters?
Sure. I don't see why not, but I'm also not aware of the difference between passing an anonymous and named function. Or the complexity in passing a function from javascript to java.

Will there be a problem if the functor (myFunc) expects a callback with a number of parameters, but those are not handled in the javascript function?
It's important to match the FunctionX signature in Kotlin, eg.

fun myfun(callback: (arg: String) -> Void)

called from javascript via

// Don't care about the string in the callback
myfun(() => console.log("invoked"))

would have to be turned into something like

new kotlin.jvm.functions.Function1({ invoke: () => { code } })

where Function1 is important, even though the javascript callback doesn't care about the returned string.

Is it possible that the functor has many overloads of different callback signatures?
Kotlin doesn't support union types and doesn't support dynamic callback signatures. It needs to explicitly know what is being passed and how many arguments are required. It's 24 classes, from Function0-23, with an invoke-method.

@PixsaOJ
Copy link

PixsaOJ commented Sep 18, 2024

@jsommr any ideas how add TS declaration for kotlin ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants