Destructive method call


#1

Hi

TL;DR
for any class with component functions, allow using spread operator(*) to extract is fields, namely:

data class Class0(val a:Int,val b:Int)
fun useInts(a:Int,b:Int)=...

val instance0=Class0(0,0)
useInts(instance0.a,instance0.b) // current approach
useInts(*instance0) // desired approach

this way, one won’t have to repeat instanceX and its field names that many times
related: Tuple Interfaces (for data classes)


I’m writing a program that needs to transfer data(class1) using udp, and store it with some additional information(class2) in memory, let’s say:

data class Class1(val a:Int,val b:Int,val e:Int) // have fewer fields so it's smaller on wire
data class Class2(val a:Int,val b:Int,val c:Int,val d:Int) // extra fields records additional data

fun checkAndBuildClass2(a:Int,b:Int,e:Int):Class2{
  if(e!=0)...
  return Class2(a,b,info1(...),info2(...))
}
val c1=readClass1(...)
val c2=Class2(c1.a,c1.b,info1(c1),info2(c1)) // current approach
val c3=Class2(*c1,0) // desired approach if we wanna use c1.e as c2.c
val c4=checkAndBuildClass2(*c1) // desired approach if we have a primitive function
  1. since data class forbids inheritance, I can’t let class2 extends class1
  2. I can write Class2(val ref:Class1,val c:Int,val d:Int) but it introduces a pointer dereference, and requires users of class2 to know about class1
    also I if don’t need class1.e, I can’t remove it in both approaches

in this example, only field a and b is shared, but in reality, there could be dozens of fields


#2

It looks really messy for me.

useInts(*instance0, 2, *instance1)

It is really hard to understand what are useInts's parameters (solution: 2 is the parameter c in first example).

Class1 and Class2 are unrelated but them must share some argument, with same types and exact positions.

Introducing some form of abstraction (like interfaces) can improve readability.


#3

yes, class1 and class2 are related, and because they’re data classes, they can’t inherit
introducing a interface automatically would solve the problem rather elegantly(see related link)
what I’m suggesting here is that there should be a way to take advantage the fact that class have components

are there other ways that one won’t have to repeatedly type instance name and their field names for related class?


and I’ve edited the post, changing the signature of useInts(…) because the original one is misleading
I meant to illustrate the usage of destructive method call but instead I give a poor example


#4

Your example are abstract (a, b, c are cryptic names).

I would use a new one:

data class Point2d(val x:Double, val y:Double)

You don’t want use the indirection for some reasons:

data class Segment(val a:Point2d, val b:Point2d)

so you prefer

data class Segment(val ax: Double, val ay: Double, val bx: Double, val by: Double)

you can fix the issue (too abstract in my mind) simply adding the function:

fun Segment(a: Point2d, b: Point2d) = Segment(a.x, a.y, b.x, b.y)

Cons: in such case you must define manually all required overload
Pro: this is invalid: Segment(*point3d, 5.0)


#5

thx, I’ll try this approach and hopefully tuple interfaces get implemented soon


#6

Just wanted to point out that Kotlin has inline extension methods like with, apply, and run that could eliminate the need to repeat the variable, so instead of:

useInts(instance0.a,instance0.b) // current approach

You could do one of the following depending on the situation:

with(instance0) { useInts(a, b) }
instance0.run { useInts(a, b) }
instance0.apply { useInts(a, b) }

with and run are equivalent and the result of the expression is the return value of useInts. apply will have a result of instance0.

There is also let and also that are like run and apply but take the value as a parameter so you could do:

instance0.let { useInts(it.a, it.b) }
instance0.also { useInts(it.a, it.b) }

#7

thx for the intel. I’m aware of this so I originally wrote

this way we can’t just use a or b because of naming collision
even for the simple case, we have to repeat a and b and every other field

on second thought, my approach is not a perfect one because it only solves part(but not all) of the problem
the tuple interface seems a good way to go, hope that one got implemented soon