0

Take a look at the following code.

fun stuff(func: () -> Int) {
    println(func().toString())
}

fun stuff(func: () -> String) {
    println(func())
}

fun main() {
    stuff { 1 }
}

This gives me a strange error.

Overload resolution ambiguity: public fun stuff(func: () -> Int): Unit defined in root package in file File.kt public fun stuff(func: () -> String): Unit defined in root package in file File.kt
'return' is not allowed here
The integer literal does not conform to the expected type Unit

I'm unsure why and trying to solve this. I could use Java Generics, but I'd like this work in pure kotlin. Could also do something with parameterized typing like

fun <T>stuff(func: () -> T)

But this isn't as elegant if the caller of stuff is unaware of what it's passing in.

I also saw this answer: Failsafe withFallback(): why kotlin compiler fails to infer lambda type?

This doesn't work due to the following error:

Duplicate method name "stuff" with signature "(Lkotlin.jvm.functions.Function0;)V"

So Based on this error I'm guessing doing this as I'd like is impossible... But I'm hopeful!

Would love some thoughts here.

  • I don't think it would work with java generics: Producer and Producer both are compiled down to Producer. I think that might also be the reason why it doesn't work in kotlin unfortunately... – Stefan Haustein Mar 29 '21 at 19:35

2 Answers2

2

That is a strange error…  When I try it in IntelliJ myself, I get the following error on both of the function declarations:

Platform declaration clash: The following declarations have the same JVM signature (stuff(Lkotlin/jvm/functions/Function0;)V)

…followed by both function signatures.  And then on the call at the end:

Overload resolution ambiguity. All these functions match.

…also followed by both function signatures.

(I don't get your last two errors at all.  Could they be from a different version of your code?)

The first of those tells you what the real problem is here: although () -> Int and () -> String are different types in Kotlin, they compile down to the same type in Java bytecode.  (That raw signature is Lkotlin/jvm/functions/Function0;, a reference to an instance of kotlin.jvm.functions.Function0, which is an interface that the Kotlin runtime uses to implement function references.  The problem is that it's generic: the return type is indicated by a type parameter.)  So both Kotlin functions compile to the same function type, and hence both versions of stuff have the same JVM signature.  Which is of course not allowed.

I'm afraid this also means that your suggestion of working around it by using generics yourself won't work either, for exactly the same reason…

In this particular case, you could write a single method for an Any parameter, as you can call toString() on anything!  But of course that won't work more generally.

(It's a general annoyance on the JVM, an unfortunate consequence of the choice of type erasure to implement generics.  You see it in all sorts of cases…)

gidds
  • 16,558
  • 2
  • 19
  • 26
1

The return type never is constituent to the method/function signature.

two methods/functions have the same signature, if they have the same name and same parameters, regardless of the return type.

This holds true for any language that allows overloading, as otherwise there would be an implicit ambiguity in constructs like stuff(stuff(...)) where it would be impossible to decide which outer stuff() to call.

Dirk Hoffmann
  • 1,444
  • 17
  • 35