-
Notifications
You must be signed in to change notification settings - Fork 362
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data objects #317
Comments
can't all |
Using the Inconsistencies:
Inconsistencies 2, 4, & 5 seem obvious at first but note that 99% of all objects have properties so adding the I understand that data classes only reference constructor properties but |
3 & 5 are not really relevant as the generated version would be functionally equivalent to the default implementation. So their omission is actually a code size optimization with no behavioral difference. The functions are defined on all types so the presence of overrides is an implementation detail that no one needs to care about. You could also argue that 2 does occur. A That same argument applies to 4. The So the only real difference is 1 and its omission is noted in the KEEP text.
[citation needed] I use objects to implement interfaces with stateless behavior and for marker types in sealed hierarchies. I would say 99% of my own objects never have properties. |
I am sorry Instead, how about this approach we automatically determine whether its a data object or not, based on the return type? data Message(String to, String from, String message)
object message = Message("[email protected]", "[email protected]", "Hello") // This now acts as "data object" as the return type is a data class.
User(String username, String password)
object user = User("KoltinUser", "S3CR3T") // Now this acts as a regular object.
|
You are correct from an implementation perspective if you base your decision on the underlying implementation details of data classes. However, this perspective doesn't seem aligned with the higher-level concept of data classes. Instead of evaluating this solution of data objects by implementation details of data classes, I want to take a step back and evaluate the higher-level concept to ensure consistency and then use that to guide implementation details rather than the reverse approach. I also want to evaluate the solution based on core engineering principles of abstraction so that names aren't misleading but instead they are consistent and provide an accurate description of what they're supposed to represent. From a concept perspective, a data class represents a container of data. The implementation details of data classes serve to meet the purpose of data classes rather than to define their purpose. For example, the implementation detail of excluding non-constructor properties from any of the auto-generated methods allows us to make the distinction of which fields should be considered to be part of the core data of the wrapper and thus only use those when checking for equality etc. therefore this shouldn't be used as part of the definition of what it means to be a data class since this is just a useful utility which makes data classes even more useful. As a test for consistency, let's take an outside perspective from a hypothetical experienced developer that isn't familiar with Kotlin. Suppose that we explain the concept of data classes and that the object declaration creates a singleton. If we then ask the engineer what they would expect a data object to be then the most natural assumption for them would be to think that a data object is a singleton wrapper of data (eg. perhaps a singleton with related constants etc.). Now continuing with this expected interpretation and given that you can never declare properties in the object constructor, this engineer would expect the class properties to be the "data" of this data object. The concept of having the data keyword generate a fairly trivial single line of code seems to have too little value so this would further make engineers wonder if something more is taking place by having a data object. This previous paragraph shows how points 2, 3, 4, and 5 are relevant based on the expected interpretation of someone that is not intimately aware of the underlying Kotlin implementation details and instead thinks in terms of the concepts that Kotlin provides. For reference, the single line of code that I was referring to on the JVM is this: Regarding exactly what percentage of object declarations have class properties, citations are probably non-existent and depend more on programming style. However, I gotta admit that my previous estimation was too hasty and high so instead of trying to put a number to it, here are some common use cases where I have properties in object declarations:
I'm sure I could add more scenarios but I think you get the idea that having val properties in an object declaration is not a rare occurrence by any means. Lastly, the amount of value added compared to the increased complexity and confusion that it introduces seems to move Kotlin in the direction of Scala especially since it breaks the clean meaning of the data keyword. |
Agree 😂 |
There's rare situation where one would like to see the default implementation of toString on objects. So personally making this default is better than adding more complexity to language itself. |
I think someone mentioned this is done so that it is backward compatible if someone was relying on the existing string representation for whatever reason If you are relying on a string representation like that - and if your app breaks on you - that is on you to fix. No need to keep luggage like this - this is not Javascript 😄 |
The reasons for introducing a separate
Also, this is just a stepping-stone in a progression of the future planned features. We do plan to introduce a more compact syntax for sealed class hierarchies for day(KT-47868 Concise syntax for defining sealed class inheritors), akin to
Here, the applicability of Moreover, we do plan to work on a better approach to objects that are used only for the purpose of namespacing several declarations together (like |
I'm eagerly waiting for this feature to become stable. Points 3 and 5 are applicable for my use case - KT-40218. |
There has been a massive improvement in the text of KEEP that now includes detailed information on the KEEP. The decisions around serialization have also been finalized (TL;DR: no special support for Java serialization, but it'll work fine with any kind of serialization thanks to the generated |
@elizarov Thanks for the update! So it won't fix KT-40218? Specifically the following case: sealed class Option<in T> : Serializable
class Some<T>(val value: T) : Option<T>()
object None : Option<Any>()
fun handleOption(option: Option<String>) = when (option) {
is Some -> "some ${option.value}"
None -> "none"
} The code above crashes after deserialization, because |
Thanks, I thought |
Can I ask for clarification about the difference between I am focusing on jvm for this question, but I'm curious about other platforms as well. Is the expected behavior the following?
That is my interpretation of the KEEP, but I'd like to have a more precise understanding of the exact scenarios when I should expect |
One of the use cases is Java serialization. After serialization you will have another instance of an Another use case I noticed is that |
Hello! I like the concept of data objects but I am bothered with questions "What if I need to introduce a property in the future? How to ensure backward compatibility for my library users' code". Example: sealed class Expr {
data object Cur : Expr() {
operator fun invoke() = this
}
data class Value(
val value: Number,
) : Expr()
data class Sum(
val expr1: Expr,
val expr2: Expr,
) : Expr()
}
fun test() {
println(Expr.Sum(Expr.Cur(), Expr.Value(1)))
} In case new property is introduced in Expr.Cur, I don't need to change test function: sealed class Expr {
data class Cur(
val context: Any? = null,
) : Expr()
data class Value(
val value: Number,
) : Expr()
data class Sum(
val expr1: Expr,
val expr2: Expr,
) : Expr()
}
fun test() {
println(Expr.Sum(Expr.Cur(), Expr.Value(1)))
} But I don't like this solution because, firstly, you can forget to call invoke function. And, secondly, it is not suitable for Java users. Maybe, you could help me to come up with more suitable solution. Thank you. |
This issue is for discussion of the proposal to add
data object
. The full text of the proposal is in this here.The text was updated successfully, but these errors were encountered: