Daniele Segato
06/19/2023, 4:50 PMdata interface GenericEntity(
val id: String,
val created: Instant,
)
data class Customer(
val name: String,
): GenericEntity
I suppose the syntax I propose here isn't the best...
Anyway, the idea would be to have the interface allow usage of copy methods as if it was a data class with just those values and ease up reuse of common data structures.
Currently there's no way to rely on the copy and other helpers methods of data classes unless you expose the implementation.Arkadii Ivanov
06/19/2023, 4:55 PMDaniele Segato
06/19/2023, 4:56 PMjw
06/19/2023, 5:03 PMdata interface
is a decent idea, but as a wholesale replacement for data class
(which should be deprecated and fired into the sun). It would describe the values that a type can provide and would automatically enable copy-like semantics (but not through problematic generation of a single function) and nominal destructuring (not positional). Basically, it would be a descriptor on top of which those two features (copy-like withers and nominal destructuring) could be built.Javier
06/19/2023, 5:12 PMdata class
, a data interface
can be easily instantiated without extending another class? if not you can have a few boilerplateJavier
06/19/2023, 5:13 PMdata interface Foo(val foo: String)
fun main() {
// should work, if not it would be annoying
Foo("hi")
}
Daniele Segato
06/19/2023, 5:14 PMDerek Peirce
06/20/2023, 8:12 AMdata class
be decomposed into more flexible ways of defining methods based on any given class's properties. For example, suppose we had the data class
toString
implemented like this:
reified fun <reified T : Any> T.customToString(): String = T::class.memberProperties.joinToString(
separator = ",",
prefix = T::class.simpleName + "(",
postfix = ")",
) {
it.name + "=" + it.get(this)
}
(Here, reified fun
would mean that the method is generated once per class, and the memberProperties
loop is unrolled at compilation time, no runtime reflection necessary.)
Then, in any class, you could declare:
override fun toString() = customToString()
Or, one could declare that functionality in an annotation, if lambdas were made to be acceptable annotation parameters:
@OverrideToString(::customToString)
and then if you allow for annotation aliasing:
alias @DataToString = @OverrideToString(::customToString)
and then a combination:
alias @DataClass = @DataToString + @DataEqualsHashCode + @DataCopy + @DataComponentN
and suddenly @DataClass
is equivalent to adding data
. Main use case that I encounter would be to allow for easily creating an equivalent of data class
that will use contentEquals
, contentHashCode
, and contentToString
for arrays (or at least respect yet another annotation on individual properties that define alternative definitions, like @DataArray val x: IntArray
with alias @DataArray = @DataArrayToString + ...
and alias @DataArrayToString = @DataToString(::contentToString)
) so that adding a single array to a data class doesn't require abandoning the concept of a data class altogether.vanshg
06/20/2023, 2:02 PMephemient
06/20/2023, 3:21 PM> What implementation would be created when calling the copy method on the interface?
The same class of the copied instance (edited)that's not a constraint that is compatible with Java's type system
Derek Peirce
06/20/2023, 10:54 PMdata class
was already largely re-inventing Lombok, except that Lombok handled arrays using Arrays.deep...
methods while data classes have no special handling for them. Lombok also only had control on the individual fields by allowing for opt-out or opt-in, with no further customization. In both cases, if you have a single field in which you want its string value in the class's toString
to not match the class itself's toString
(such as with arrays, or if you want some truncation), you must abandon the entire concept and generate your toString
entirely manually, with all the code bloat and risks of not updating that come with it. (To be clear, I'm not entirely attached to having annotations that add functions to classes, though it would require some new syntax to ensure that multiple methods can be generated together, particularly important for equals
and hashCode
.)elizarov
07/12/2023, 8:38 AMvalue class
concept that is just like data class, but readonly, identityless, and without positional destructuring. Since copy
methods are harmful for library evolution, they’ll be replaced with evolution-friendly copy var
concept. The data interface
that you want will be available as value interface
. See this document for full details on this vision: https://github.com/Kotlin/KEEP/blob/master/notes/value-classes.mdArkadii Ivanov
07/12/2023, 8:56 AMcopy
method is copying the state in reducer-like functions:
fun reduce(state: State, msg: Msg) : State
I really hope this use case will be supported by value classes as well, if they are going to replace data classes. In other words, obtaining a new reference with some properties changed, and with as little boilerplate as possible.elizarov
07/12/2023, 8:57 AMJavier
07/12/2023, 10:21 AMcopy
returns a different instance of the state
fun addTag(tag: String) {
state.lastUpdate = now()
state.tags += tag
notifyOnChange(state)
}
here, where is the new instance? If the new instance comes from the operation with that sugar, then we would need:
fun addTag(tag: String) {
val stateUpdated = ((state.lastUpdate = now()) + (tags += tag))
notifyOnChange(stateUpdated)
}
If there is a local state which is changed, the sugar is not exactly the same than copy