Hi all! Just started learning Kotlin, enjoying it ...
# getting-started
d
Hi all! Just started learning Kotlin, enjoying it so far! 🙂 I’ve only done some coding via the Kotlin playground but I had a couple of questions. 1. When you have a lamda assigned to a variable, is there a way to get the compiler to say the “name” (the variable name) of a lamda function when errors occur? Right now, I get the below:
Copy code
// This code
val add: (Int, Int) -> Int = { x, y -> x + y }

fun main() {
    add(1, 2, 3)
}

// Gives me
Too many arguments for public abstract operator fun invoke(p1: List<Int>): List<Int> defined in kotlin.Function1
Versus with a named function
Copy code
//This code
fun add (x: Int, y: Int): Int = x + y

fun main() {
    add(1, 2, 3)
}

// Gives me

Too many arguments for public fun add(x: Int, y: Int): Int defined in root package in file File.kt
Side note: I don’t know why the former error message has this type annotation for the function (it doesn’t match what my code specifies):
fun invoke(p1: List<Int>): List<Int>
2. Is there a way to type a named function via extracting the types, like you can do with variables + lamda/anonymous functions?
Copy code
// Lamda with inline type annotations
val add1 = { x: Int, y: Int -> x + y}

// Lamda with extracted type annotations
val add2: (Int, Int) -> Int = { x, y -> x + y}


// Named function with inline type annotations
fun add3 (x: Int, y: Int): Int = x + y

// Named function with extracted type annotations
?
This would be helpful so we could easily share type annotations among functions via a typealias. In addition this is particularly helpful for me because I find it harder to parse type annotations when they’re inlined. And I often like to be able to glance at function types and see if it matches what I need versus having to pick it apart from parameters. Thanks!
j
I believe what you're experiencing is just a matter of habit. Coming from a Java background it's pretty usual (and quick) to identify a method's signature when looking at its declaration despite the presence of argument names. Using this style with lambdas and fully declared types is not idiomatic, and probably considered bad style in most cases (some particular use cases may call for this though, like mutable callback properties). Your experience with compiler errors will indeed not be ideal if you try to use the language this way.
I don’t know why the former error message has this type annotation for the function (it doesn’t match what my code specifies)
This is not the error I get when pasting your code in the playground, so I would say it's probably other code lying around that was interfering. What I get with your first snippet is (correctly):
Copy code
Too many arguments for public abstract operator fun invoke(p1: Int, p2: Int): Int defined in kotlin.Function2
When you have a lamda assigned to a variable, is there a way to get the compiler to say the “name” (the variable name) of a lamda function when errors occur?
No. When storing a function (anonymous or not) in a variable, the call to the function through the variable
myVar()
is just syntax sugar for a call via
invoke
operator:
myVar.invoke()
. This is why you get this error message.
Is there a way to type a named function via extracting the types
Not really. You can assign a reference to a named function to a variable with a type, but that would be really awkward style (apart from specific contexts in which it might be necessary):
Copy code
fun add(x: Int, y: Int): Int = x + y

val myProp: (Int, Int) -> Int = ::add
And that still wouldn't help with the problem mentioned above, because it's still calling a function through a variable. You could also make the function anonymous and assign it to a property/variable with explicit type, but that would also be very awkward style and it would still not fix the problem mentioned above:
Copy code
// please don't do this
val add: (Int, Int) -> Int = fun(a: Int, b: Int): Int = a + b
I often like to be able to glance at function types and see if it matches what I need versus having to pick it apart from parameters.
Functions are really more than their types. You can't just use the type to decide whether it's what you need.
(Int, Int) -> Int
could be anything:
add
,
max
,
min
, or even
fun runVirusAndGetExitCode(nbFilesToDelete: Int, cpuToBurn: Int): Int
d
@Joffrey Thanks for replying! And thanks for letting me know a bit more about what’s conventional/idiomatic. I really like the lamda syntax but yeah, it has some drawbacks and restrictions! And yes, I am just used to being able to annotate types outside of functions. Aside from it being what I’m used to, it does have practical benefits like sharing function type annotations like below:
Copy code
typealias itemOperation = (Item, List<Item>) -> List<Item>

val addItem: itemOperation = { item, items -> /** ...  */ }
val deleteItem: itemOperation = { item, items -> /** ...  */ }
With the type alias and lamda functions, I don’t have to rewrite that same type annotation 2 (or more) different times and the relationship among the functions is super clear right away. I believe these features to be genuinely helpful for when you’re writing in a functional programming style. But, I suppose that’s just a no-go in Kotlin for now! I’m hoping that option might make it’s way into the language at some point but that won’t stop me from enjoying Kotlin right now 🙂 Thanks again for the detailed response, I appreciate it! 🙌
j
Maybe it's my Java background talking, but what you're looking for really feels like interfaces to me
d
@Joffrey Do mean functional interfaces? These look interesting! I’m not sure I quite understand what the advantage is aside from a little nicer error message (but still not specific to the
val
name (
add
):
Copy code
fun interface IntOperation {
   fun op(x: Int, y: Int): Int
}

val add = IntOperation { x, y -> x + y }

fun main() {
  add.op(7, 2, 3)
}

// Gives me
Too many arguments for public abstract fun op(x: Int, y: Int): Int defined in IntOperation
What are the idiomatic usages of functional interfaces?
g
You could for example rename your method to
invoke
and add the
operator
modifier, that will allow you to use a function call syntax, and still have a good compiler error message:
Copy code
fun interface IntOperation {
    operator fun invoke(x: Int, y: Int): Int
}

val add = IntOperation { x, y -> x + y }

fun main() {
    add(7, 2) // compiles fine
    add(10, 10, 10) // Compiler error: Too many arguments for public abstract operator fun invoke(x: Int, y: Int): Int defined in com.amplitude.integrations.connector.IntOperation 

}
d
@Gabor Torok Ooh, this looks nice! Thanks for showing me this 🙌 I’m assuming most Kotlin devs write functions the “standard” way (
fun myFunction()
). When would it be idiomatic to use functional interfaces and write functions like this? What are the accepted use cases? Thanks!
g
mostly when you would like to implement higher-order functions. also, this is just better syntax for the concept that already exists in java since jdk8, functional interfaces. if you check out how and where they are used, it could also give you some ideas.
âž• 1