Is there a proposal to support "overloads by retur...
# language-proposals
d
Is there a proposal to support "overloads by return type"? Something that would allow the following to compile:
Copy code
class A
class B

fun make():A = A()
fun make():B = B()


fun main() {
    val a:A = make()
    val b:B = make()
}
My actual use-case looks a little more like the following:
Copy code
package com.stochastictinkr


class A {
    fun configureA() {
        println("Configuring A")
    }
}

class B {
    fun configureB() {
        println("Configuring B")
    }
}

inline fun make(block: A.() -> Unit = { }): A = A().apply(block)
inline fun make(block: B.() -> Unit = { }): B = B().apply(block)

inline fun accepts(a: () -> A, b: () -> B) {
    println(a())
    println(b())
}

fun main() {
    accepts({ A() }, { B() })
    accepts(
        {
            make {
                configureA()
            }
        },
        {
            make {
                configureB()
            }
        },
    )
}
e
overloading by lambda type already works, you just need to give then different JvmName
overloading by return type only… while technically possible on JVM, I'm pretty sure it'll break plenty of things
d
No, it doesn't work:
Copy code
e: file:///Users/pittsd/dev/RandomStuff/src/main/kotlin/com/stochastictinkr/Broken.kt:30:9 Type mismatch: inferred type is Unit but A was expected
e: file:///Users/pittsd/dev/RandomStuff/src/main/kotlin/com/stochastictinkr/Broken.kt:31:13 Overload resolution ambiguity: 
public inline fun make(block: A.() -> Unit = ...): A defined in com.stochastictinkr in file Broken.kt
public inline fun make(block: B.() -> Unit = ...): B defined in com.stochastictinkr in file Broken.kt
e: file:///Users/pittsd/dev/RandomStuff/src/main/kotlin/com/stochastictinkr/Broken.kt:32:17 Unresolved reference: configureA
e: file:///Users/pittsd/dev/RandomStuff/src/main/kotlin/com/stochastictinkr/Broken.kt:35:9 Type mismatch: inferred type is Unit but B was expected
e: file:///Users/pittsd/dev/RandomStuff/src/main/kotlin/com/stochastictinkr/Broken.kt:36:13 Overload resolution ambiguity: 
public inline fun make(block: A.() -> Unit = ...): A defined in com.stochastictinkr in file Broken.kt
public inline fun make(block: B.() -> Unit = ...): B defined in com.stochastictinkr in file Broken.kt
e: file:///Users/pittsd/dev/RandomStuff/src/main/kotlin/com/stochastictinkr/Broken.kt:37:17 Unresolved reference: configureB
Even when using JvmName
e
ah resolution doesn't have a way to disambiguate with receiver type
but
make(A::configureA)
should work
d
Yeah, but this is a toy example. The actual code would call several methods and set properties on A
e
other ways to disambiguate include
Copy code
make(fun A.() { configureA() })
or giving the arguments different names so you can
Copy code
make(blockA = { configureA() })
or not using the receiver to disambiguate
Copy code
make { a: A -> a.configureA() }
changing the signature of
make
in the latter two
d
Admittedly, this is kind of an x/y problem. What I'm ACTUALLY trying to do is provide a way for a method to accept either an existing resource, or one that is allocated within the context of the method call.
Copy code
package com.stochastictinkr

class A
class B

class ObjectPool : AutoCloseable {
    override fun close() {}
}

inline fun ObjectPool.allocate(configure: A.() -> Unit = { }): A = A().apply(configure)
inline fun ObjectPool.allocate(configure: B.() -> Unit = { }): B = B().apply(configure)

inline fun accepts(a: context(ObjectPool) () -> A, b: context(ObjectPool) () -> B) {
    ObjectPool().use { pool ->
        println(a(pool))
        println(b(pool))
    }
}

fun main() {
    val existingB: B = B()
    accepts(
        {
            allocate {
            
            }
        },
        { existingB },
    )
}
For what its worth, A and B are actually really annoyingly long names like
VkPhysicalDeviceMemoryProperties.Buffer
and
VkAccelerationStructureGeometryMotionTrianglesDataNV
And there are 1312 different ones 😉
In any case, I can do things to kind of make it work, but it would all be simpler if we could have overload resolution by return.
e
my general thoughts are I I have seen nobody implement a sane (complete, fast, and predictable) type inference system for an object oriented language with subtyping and overloads. Java went with type propagation, Rust went without subtyping, OCaml went with requiring explicit types when the solver gives up around OO
I don't know where the boundaries are so whether making Kotlin handle this world be tractable or not
d
I mean, C++ could probably do this, but if this was C++ could, I wouldn't need to 😉
e
but it's only fast because it's limited in scope
https://blog.polybdenum.com/2020/07/04/subtype-inference-by-example-part-1-introducing-cubiml.html
The most popular form of type inference is based on the Hindley-Milner system, which is limited by its lack of support for subtyping. … Hindley-Milner style type inference runs in approximately linear O(n) time for monomorphic code. Cubic biunification by contrast has worst case cubic O(n³) time complexity, hence the name. Subtype based inference is more powerful, but that power comes at a price, as the compiler has to do a lot more work.
it does seem that state of the art has progressed a bit since I last looked
d
Interesting. I've been interested in understanding type system implementations in more detail, I'll have to give that a read.
e
still, Kotlin is basically Java-style with a bunch of additional hacks features, so it's hard to compare
d
Maybe I really just want to code in lisp 😉