Proposal: conversion method "into"

Allow easy conversion between types. E.g.

fun String.into(): URL = URL(this)

The call site could look like

fun sendRequest(url: URL) {}
sendRequest("https://example.com".into())

In case of diamond problem, it could be resolved by explicitly stating the type

interface A
interface B: A
interface C: A
fun SomeClass.into(): B {}
fun SomeClass.into(): C {}

fun foo(a: A) {}

fun main() {
    val s: SomeClass
    foo(s.into()) // Ambiguity exception
    foo(s.into<B>()) // OK
    foo(s.into<C>()) // OK
}

Or even allow implicit casting in a block:

fun String.into(): URL {}
fun sendRequest(url: URL) {}

fun main() {
    autoConvert(URL::class) {
        sendRequest("https://example.com"/*Automatically into'd into URL*/)
    }
}

Edit:
This can also maintain Java interoperability by using the toX() pattern, A.into(): B would be A.toB()

Doesn’t this just work with Kotlin as-is?

Automated coversions are very often a bug farm. Kotlin design went for not doing any automated conversion.

4 Likes

Doesn’t this just work with Kotlin as-is?

Well if you want to achieve the demonstrated method, it

  1. requires a hacks to check what type it is, and then return to it with a cast, like the pattern get(Class<T>): T
  2. Doesn’t allow for externally declared conversion methods, like String.into(): URL in the example
    Edit:
    You can do something with reflection and get “to$class.simplename”, but that means it is resolved at runtime instead of compile time, and additional checks have to be done, instead of it being statically resolved at compile time. Which is, again, a hack and doesn’t allow for extension.

Automated coversions are very often a bug farm. Kotlin design went for not doing any automated conversion.

Indeed it is, that’s why I didn’t say it should be fully automated, the idea behind the block is to only allow a certain type to be automatically converted, similar to the idea of with, though I can understand why it might not be the part of the final addition, which is why I said “or even” :thinking:

This (well, without the automatic conversion part) is in fact a special case of a more general pattern – allowing functions with signatures which are identical except for the return type, disambiguating between them by expected and/or explicitly mentioned return type at call site – right?

1 Like

Well, not exactly, it can be trivially compiled into JVM like “toA”, “toB” etc, it’s just such a commonly used pattern I think there should be some standard to it. I wouldn’t put it in the same basket exactly, since resolving the collision isn’t the main point here.

Hm, you say resolving the collision isn’t the main point, but unless I’m missing something this collision is the only reason why this (again, excluding the auto-conversion part) can’t be done in Kotlin currently, right?

1 Like

Well, not exactly, unless return type collision is clarified via a type parameter(which I doubt it would), it wouldn’t be the same.
Im more inclined to think of it as fun < T > into(): T which is then dispatched at compile time.

What’s wong with myString.let(::URL)? It’s certainly a little longer than URL(myString) but it shares the nice left-to-right of the .into() proposal that the direct constructor call lacks.

The String → URL example is a constructor call and those shouldn’t be more hidden than necessary.

What I miss far more (and just dealt with a few days ago with an inline that I certainly wouldn’t mind seeing in the standard library) is casting in “dot order”:

(x as Y) is certainly an improvement over java’s parentheses-hell of ((Y)x) and as? beats the java equivalent by an even larger margin, but they still require jumping back for parentheses if x is a complicated expression.

x.asOrNull<Y>()?.someYMethod() would work nicely left-to-right no matter how complicated x actually is. (the as-sans-? variation any.asOrCrash<Y>() might seem a little overdramatic in naming, but I do like the explicit “OrNull” in autocomplete lists)

1 Like

You can do

fun String.toUrl(): URL = URL(this)

then you can do this:

sendRequest("https://example.com".toUrl())

Not everything can be converted this way, the example was only for illustrative purposes anyway.

The proposal is not about changing the casting system, it’s to standardize the toX pattern that was used a lot.

That’s exactly what this pattern was aiming to solve D: the toX pattern is commonly used, and standardising it on a language level could be nice. Especially since this way, the type can be implicit. But the toX would be how its presented when interoperating with Java :wink:

If I understand you correctly, you would like for the compiler to automatically generate these functions for you?
For example:

// say I have a custom class
class Point {
    var x = 0
    var y = 0
}

// and I have another class
class IntPair {
    var first = 0
    var second = 0
}

How would the compiler know how to convert between the two classes?

val point = Point()
val converted: IntPair = point.into() // how would the compiler generate .into()?

If I have a vastly different class, how would the auto-generation of these conversion methods work?

class User(
    val name: String,
    val age: Int,
    val height: Float
)

val user = User("Josh", 24, 172.3F)
val converted: IntPair = user.into() // how?

No… I want compiler to decide which method to call…
The user would define
fun Point.into(): IntPair = IntPair(x, y)
fun Point.into(): Pair<Int, Int> = x to y

fun foo(pair: IntPair) {}

val point: Point

foo(pair.into()) // automatically calls the one that returns IntPair

which is stated quite clearly in my original post imo.

If I understand correctly, does that mean you want the standardization of conversion methods and the benefit of not having to type potentially longer name like .into() instead of .toURL() or .toJSON()?

Because you also still need to manually write the conversion methods.

Edit: I just now understood your intention

IMO the benefit is trivial and isn’t worth standardizing.
I only see the point in standardizing conversions if they enable implicit conversions so you don’t even have to type .into() like you can do in C++
But again, the Kotlin team has decided to not allow implicit conversions

That is correct, the point is just to unify all conversion methods into one method, Any#into(). Rust has a trait for this purpose. See Into in std::convert - Rust

1 Like

I think this works well for Kotlin because despite not using a trait system, the extension methods can be used to achieve the same effect.

I think it would make sense to consider use cases and the core problems it solves.

For me, I don’t see an into operator or adding it as a member of the Top class Any to be strong enough to overcome the minus 100 points rule. Yes, it has some pros but it’s not so beneficial on its own to be convincing.

Instead, a trait system, type classes, and other more powerful language concepts that would enable this style of conversion might be a better consideration.

Fantastic point, I had a similar feeling when I was thinking about the system. I wouldn’t be that upset either if it doesn’t get added, I just thought it could be a useful feature and made a proposal. :stuck_out_tongue:

Annotate single-arg constructors to generate an .into or a .toX extension function with the parameter type as receiver?

I’m decidedly in the camp of those who wouldn’t like it at all to see this generated automatically for all single-arg constructors, because there are plenty of cases where the semantics are completely different (e.g. a mutable class with some form of owner/patent reference, that’scertainly not “the same data, but in a different shape”). Perhaps default for data classes?

And in the question of .into vs .toX, I’d prefer .toX by a wide margin. Because reading is important (reading would be one of the prime benefits of this entire thing, y.toX() would read better than X(y), particularly for non-trivial expressions in the place of y)

1 Like

I agree with @tom000 . If Kotlin would support overloading functions by their return type, then I think your into() would be possible and it would become just a convention. I’m not sure, what do you mean here: