Generic copy for generic data classes. If I have a generic data class: `data class Basket<Fruit...
n
Generic copy for generic data classes. If I have a generic data class:
data class Basket<Fruit>(fruit: Set<Fruit>)
And an instance of it:
val bananas : Basket<Banana>
The type checker does not allow:
val apples : Basket<Apple> = bananas.copy(fruit = listOf(apple1, apple2))
Instead, I have to call the constructor of new Basket<Apple> explicitly and copy fields from the Basket<Banana>. What do people think about making the copy method generic for generic data classes?
j
How would it work if you have 2 properties using the generic type?
Copy code
data class Basket<F>(val fruits: Set<F>, val rottenFruits: Set<F>)

val bananas: Basket<Banana> = TODO("some basket of bananas")

val apples = bananas.copy(fruit = setOf(apple1, apple2)) // rottenFruits is not replaced here, so still bananas
By definition,
copy
is supposed to keep all other properties the same, so it wouldn't play nice with multiple generic properties.
n
That would be a type error, just as if you tried to call the constructor with two sets of different types
j
That would make it different from other generic functions. They normally don't allow defaults values that could disagree with the type parameter
n
I’m saying it wouldn’t allow default values that disagree with the type parameter. If you try to do that, it would be a type error.
j
My point is that this is forbidden at the declaration site for any generic function right now. What you're proposing here means that
copy
would be allowed to declare default values that disagree with the generic type parameter, and it would only be forbidden to use these default values by not providing arguments on the call site. That's not how other generic functions work, so it would be inconsistent unless we allow this for all generic functions
n
Only if copy is implemented by a single function.
🤔 1
j
Could you please show how you envision
copy
(or `copy`s) to be defined for the
Basket
data class that I provided above?
n
What I’m proposing is the behaviour of code that invokes copy on a data class. The compiler would have to map that to functions / JVM bytecode / etc. in order to enforce type safety.
It would be something like…
Copy code
data class Basket<T> ... {
    fun copy(fruit: Set<T>) = Basket(fruit,rottenFruit)
    fun copy(rottenFruit: Set<T>) = Basket(fruit,rottenFruit)
    fun <U> copy(fruit: Set<U>, rottenFruit: Set<U>) = Basket(fruit,rottenFruit)
}
j
That leads to conflicting overloads, I think the non generic copy has to have default values
n
If the generic copy was called something different (eg “map”) that would also be fine.
I’ll have to check the bytecode. I thought copy was not actually implemented as a function but rather the compiler generated a call to the data class constructor at the call-site.
j
I'd certainly use this if the potential issues could be worked out. I have to manually make a function like this quite often.
j
> I thought copy was not actually implemented as a function but rather the compiler generated a call to the data class constructor at the call-site. > No copy is a real synthetic function, which makes it a binary compatibility problem, by the way