Signature method considerations

I’d love to have the possibility to type

val i: Int = get()

Where

fun get(): Int = 0
fun get(): Float = 0f

Question that arises: what to do when you simply call get()?

Simple, similar as today when you try to call the same when you would have

fun get(i: Int = 0): Int = 0
fun get(f: Float = 0f): Float = 0f

It’s forbidden because of ambiguity

We could replicate what Swift already does

func some() -> Bool {
    return true;
}

func some() -> Int {
    return 1;
}

let valBool: Bool = some()
let valInt: Int = some()

some() // this doesn't work: Ambiguous use of 'some'
4 Likes

I don’t think that a feature such as that is very useful. Did you consider generics? Maybe it is enough?

I believe this is a limitation of the JVM–Maybe using something like the @JvmName annotation would allow this to compile for the JVM. See: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#handling-signature-clashes-with-jvmname

1 Like

We are working on a wrapper for opengl (here) and we have tons of methods that return different types.

The file I linked is still incomplete, there are many inline classes to add…

How would you implement generics?

According to @gildor:

JVM includes return type to method signature, but because Java doesn’t, It wouldn’t be possible to support in Kotlin because of interop

It’s just a design decision (for the moment hopefully)

2 Likes

The limitation could a compiler manage. Important is which names the method finally have in the byte code.

1 Like

Sorry, you are right. With generics you can not achieve it. I thought something like that…

fun <T> some(): T { ... }

...
val r = some<Int>();
val r2 = some<Boolean>();

OpenGL is normally a C library and there are no equal function names, btw. C prohibits it. In which function are you thinking for example?

All of these ones

Yeah… I never liked the OpenGL API since 3.0 and higher…

So I see what you mean. As I saw for short, you are designing higher level functions, not just exposing the C functions?

inline fun <reified T> getTexLevelParameter(target: TextureTarget, level: Int, name: TexLevelParameter): T

As I said, there are different functions but you want them unite into one function name. So you did it with generics?
On other side… there is the red book(?) which is teaching OpenGL with these C functions. How does your implementation fits in? Sorry I didn’t read your code, just some snippets… :slight_smile:

Uh, why that? Things got pretty nice especially with the Direct State Access

Yeah, exactly.

Yeah, at the moment the only way I could find was to use inline reified methods.

This is basically a wrapper to reduce boiler plate code and make it less error prone as possible with inline enums, inferring automatically mandatory parameters, object-oriented design, etc etc

The logic behind remains always the same as you’d write vanilla opengl though :slight_smile:

Uh, why that? Things got pretty nice especially with the Direct State Access

The control is of course nice, and it makes everything faster. But the design of these functions is not very pretty. For example all these query functions have a strange semantic.
I tried Vulkan and I rewrote the tutorial example in C++. The query functions in Vulkan seem very clean. The semantics are all over similar. Well I don’t like the C Style functions and I wrapped them in C++ functions.

Back to kotlin… I think, you cannot implement it better. Or you put an additional parameter. But that’s not really better.

object AsInt
object AsBool
fun f(p:AsInt):Int=0
fun f(p:AsBool):Boolean=false
...
@Test
fun foos() {
  Assert.assertEquals( 0, f(AsInt) )
  Assert.assertEquals( false, f(AsBool) )
} 
2 Likes

I don’t know OpenGL, but reading this thread I have a few suggestions:
Instead of creating separate objects like asInt etc. just create an enum with these values and make function an extension of this enum.
Then instead of f(asInt) you can call asInt.f() or even Int.class.f() without defining enum.

Also in this case you don’t need to do “when”, but just implement type-specific function.

This sounds like a use case for Sealed Classes.
sealed class Getter {

  data class GetInt(val i: Int) : Getter()
  
  data class GetFloat(val f: Float) : Getter()

}

Just a short info, I’m experimenting right now with

glm.e.float, glm.e.double, etc etc

where

object glm {
    object e {
        val float = 2.718281f
        val double = 2.718281828459045
    }
}

I just entered the same problem with some functions in an API where I wanted to use the labels as indicators of expected values although the Type of these values were the same.

eg:

    fun function(length: Int) {}
    fun function(height: Int) {}

We do that in Swift, on the basis of ObjC, SmallTalk…
So it clashes.

I change for another type

    fun function(length: Int) {}
    fun function(height: UInt) {}

But Int & UInt are seen as same.

As my problem was in constructors and not in methods, I didn’t have the opportunity to rename functions as

    fun functionLength(l: Int) {}
    fun functionHeight(h: Int) {}

So I had to adopt an ugly workaround

    constructor(length: Int) {}
    constructor(height: Int, discriminator: Unit = Unit) {}

to avoid the clash.

Why haven’t adopted label discrimination in polymorphism on Kotlin that own labels remain a total mystery to me.

PS: the discussed solution using generics to solve a so little semantic problem looks a bit out of dimension — even I do like to use them when it’s relevant.

And you just listed all and only languages in the world that do this :stuck_out_tongue_winking_eye: But joking aside, there is no mystery here. We shouldn’t expect language X do something simply because language Y does this. Languages you mentioned have a different concept of “calling a function” than most of the languages, so they needed to design this differently.

Also, this is a different problem than in the beginning of this topic. Overloading by param names feels more “exotic” to me than by a return type.

Anyway, what should happen if we do: function(10)?

Languages that include labels for params doesn’t allow to call functions without param labels. Not like kotlin where labels are only param names and are optionals in the call.

In swift you can have

func function(length: Int) {}
func function(height: Int) {}

you call

function(length: 10)
function(height: 10)

If you want to avoid to ‘say’ the label, you use the elision.

func function(_ length: Int) {}

but you can have only one function(_ xxxx: Int) {} because it would be impossible to define which is targeted.

Swift hasn’t a different concept of “calling a function” than the most of existing language.
It just integrated the characteristics of discrimination by labels for polymorphism if they are present.

Yet I definitively don’t understand why kotlin didn’t adopt that, that is definitely a very modern approach, that so simplify writing and make reading so mandatory readable.
It’s so true that IDE like IntelliJ displays labels even though you didn’t put it.
And the reason lies on the basis of smalltalk that bring about the paradigme of “talks”.

fun add(length: Int) {}
fun add(height: Int) {}

fun add(length: Int, andHeight height: Int) {}

give a concise approach of param meanings, and readability.

An option is:

class Test(length: Int = 0, height: Int = 0)

val l = Test(length = 12)
val h = Test(height = 12)
1 Like
fun main() {
    function(length = 5)
    function(height = 10)
}

object Unit1
object Unit2

@JvmName("functionLength")
fun function(length: Int, unit: Unit1 = Unit1) {}
fun function(height: Int, unit: Unit2 = Unit2) {}
1 Like

Excellent!

I extend:

class Test(length: Int? = nil, height: Int? = nil)

So we can define which to consider.

1 Like

ok @kyay10

You discrimine by ‘void’ object. Nice solution too. Thanks.

1 Like