I have question about language design.
Why not to differentiate functions by return type?
What I mean:
val node: Int = getNode(i)
val node: Node = getNode(i)
fun getNode(id: Int): Int { ... }
fun getNode(id: Int): Node { ... }
We can’t define this two functions because they have same signatures. So why no to differentiate it by return type? For example in case when two functions have same signature val should be declared explicitly (because type inheritance not works in this case).
It would lead to ambiguities when you do not assign it to a value. For example:
fun aFunction(i: Int) { ... }
fun aFunction(n: Node) { ... }
aFunction(getNode(nodeId))
Which getNode(...) function should the compiler invoke here? It must choose 1. The compiler does not know the actual type of the node at this point.
In your case I would simple use different function names. This assumes you know that the node with the given ID is of the returned type:
fun getInt(id: Int): Int { ... }
fun getNode(id: Int): Node {...}
val intNode = getInt(i) // Inferred type: Int
val node = getNode(i) // Inferred type: Node
If you do not know the type of the node beforehand, you have to check the actual type of the returned value:
fun getNode(id: Int): Any { ... }
fun process(value: Int) { ... }
fun process(value: Node) { ... }
val node = getNode(nodeId)
when (node) {
is Int -> process(node)
is Node -> process(node)
else -> ...
}
I do not understand. Closures have a distinct type, and a variable/parameter can only hold 1 specific type of closure. Can you show code where closures result in an ambiguity?
When inheritance comes into play it would also not work any more. Methods with same name and parameter list must have the same return type. This is the case in all OO languages I know of. I don’t know exactly the reason, but I guess it is because type matching algorithms would get to complicated, make the compiler slow or something like that.
What kind of problem with inheritance? Could you please post some example like @jstuyts did. Because for now it doesn’t looks clear from your answer. Thanks!
Well, it is about methods of same name and parameter list that are in the same inheritance tree (one overwriting the other). They must also have the same return type. As already said, there is no language I know of that would allow for it except dynamically typed languages and Objective-C (some weird hack to remain in the spirit of Smalltalk)
Your proposal does not add much (if any) value. As Kotlin is strongly typed, you already know which function you are going to invoke when writing the code. Having the same name for functions with different return types, adds the requirement to add a type to many (if not all) invocations of those functions.
What do you gain if you are able to write the following code:
fun getInt(id: Int): Int { ... }
fun getNode(id: Int): Node { ... }
val i1 = getInt(id1)
val i2 = getInt(id2)
val i3 = getInt(id3)
val n1 = getNode(id4)
val n2 = getNode(id5)
val n3 = getNode(id6)
As this:
fun getNode(id: Int): Int { ... }
fun getNode(id: Int): Node { ... }
val i1: Int = getNode(id1)
val i2: Int = getNode(id2)
val i3: Int = getNode(id3)
val n1: Node = getNode(id4)
val n2: Node = getNode(id5)
val n3: Node = getNode(id6)
val map = mapOf<Int, Node>(getNode() to getNode())
I think the code above is unclear. I would expect Int to be a subtype of Node, or vice versa, because the same function is used for both the key and the value.
This is what it looks like with different function names. It is much clearer if you ask me:
val map = mapOf(getInt() to getNode())
Your second example does not make sense because obj:getNode will result in a function reference, and you want to map from Ints to Nodes.
A closure expression can result in different values depending on the type of variable or parameter it’s being assigned to. So val x = { it.toString() } is invalid because it is ambiguous, while val x: (Int) -> String = { it.toString() }is valid. But the expression { it.toString() } can be many other types (Java functional interfaces, etc.).
Likewise, there’s nothing preventing a functional call expression to refer to different functions depending on the context (Rust has this, I believe).
That being said, I’m not defending it’s a desirable feature for Kotlin.
Yeah, syntax was wrong, but anyway syntax not the case I’m still do not see any good reason that prevents this feature. And as @ilogico I’m also do not say that it is very good feature for language and should be implemented.