Wouldn't it be nice if we could destructure into f...
# language-proposals
n
Wouldn't it be nice if we could destructure into function parameters?
Copy code
fun getMarkerLocation(): Pair<Double, Double> { ... }

fun addMarker(name: String, lat: Double, lng: Double, color: Int) { ... }

addMarker(
    name = "marker",
    color = Color.RED,
    (lat, lng) = getMarkerLocation()
)
I wonder if this has ever been discussed.
7
n
just wanted to chime in that I like this a lot. Easy to guess what it does based on available destructuring syntax, very non-intrusive to the rest of the language since it's only happening in this one case, saves you creating local variables and calling a function separately just because you happen to be passing forward 2 things instead of 1
n
It would also be super helpful with secondary constructors, where creating a local variable is not even an option.
Copy code
fun getDefaultMarkerLocation(): Pair<Double, Double> { }

class Marker(val lat: Double, val lng: Double) {
    constructor() : this((lat, lng) = getDefaultMarkerLocation())
}
Currently the only way to do have this is to add another constructor or use a factory function instead.
n
yes, that's a good point
Just so you're aware, since you didn't explicitly mention it, another workaround is to use a "smart constructor"
Which is an overload of the invoke operator for the companion object
n
That's a nice idea, I have never seen it. Thanks!
n
it's also necessary if you have a generic constructor. It's a bit of a weird dance but smart constructors do actually solve all the major issues with secondary constructors from my perpsetcive
np 🙂
g
Or you can create a data class Coordinates and pass it everywhere without destructuring (it even can be an inline class where lat and long saved to Long/Double), it looks as better solution for this particular case then wrap/unwrap lat/long to pair Also, it looks that it requires named destructuring, otherwise you can get very nasty bugs if swap lat and lng in addMarker constructor (and it would be not very useful without it anyway)
n
Yeah, it should only work with named params, so that we leverage a very familiar syntax. So familiar that I have actually tried to do this instinctively, before realizing that it is not implemented 😄 I can see that there are several possible workarounds, but having language support for this would feel just right to me
g
leverage a very familiar syntax
That is a problem, that this syntax already used for destructuring, but for position destructuring, not named one, so they have different behavior depending on call context
n
I'm not sure what you mean by named destructuring? The named params are those of the function being called, the object would be destructured based on the position with
componentX()
functions as always.
g
componentX is positioned destructuring
So with current syntax val (lat, lng) = getMarkers() val (lng, lat) = getMarkers() Is the same, but if it would be true for function arguments it wouldn't be very useful, and error prone
n
Yes, your
(lng, lat) = getMarkers()
would be equally wrong in a
val
assignment than it would be in a function call with named params. We're not introducing any new error
g
Yes, you right, it doesn't make destructuring worse, but in case of local variables it's not a big thing, you don't care about order of variables, but in case of constructor you do, your destructuring should always follow constructor arguments order, which wouldn't allow to use this syntax in many cases, named destructuring doesn't have such problem
n
In my idea you don't need to follow the constructor arguments order because they have a name. You can have
Copy code
data class Location(val lat: Double, val lng: Double)
data class LocationSwapped(val LNG: Double, val LAT: Double)

val location: Location = getLocation() // whatever
val locationSwapped = LocationSwapped((LAT, LNG) = location)

assertEquals(location.lat == locationSwapped.LAT)
assertEquals(location.lng == locationSwapped.LNG)
And it would work fine, because
LAT
and
LNG
names in the destructuring operation match the names of the
LocationSwapped
constructor. You wouldn't be allowed to use a different name that does not match any argument, I can't call LocationSwapped((XXX, YYY) = location) because the constructor does not have XXX and YYY parameters. It only has LAT and LNG. So in this example
LocationSwapped
is created with named parameters , while
Location
is destructured positionally, as always. Just like calling
LocationSwapped(LAT = location.component1(), LNG = location.component2())