Hi everyone! :wave: I'm wondering if there's any ...
# language-evolution
r
Hi everyone! đź‘‹ I'm wondering if there's any ongoing proposal or discussion around introducing a
readonly
-like keyword for class properties in Kotlin. For example, something like this:
Copy code
class ComponentDTO(
    val primaryText: String,
    val secondaryText: String? = null,
    readonly val category: Category = Category.Informative,
    readonly val drop: Drop = Drop.Down
)
Which would internally be equivalent to:
Copy code
class ComponentDTO(
    val primaryText: String,
    val secondaryText: String? = null,
) {
    val category: Category = Category.Informative
    val drop: Drop = Drop.Down
}
I think this could be particularly useful, especially if it can be implemented without breaking binary compatibility when removing the
readonly
keyword. Would love to hear your thoughts or if there's something similar already being considered!
c
what is the expected behaviour?
val
already provides read-only semantics (the reference can’t be changed).
a
If the second example it would turn into, why not just write that then?
c
especially if it can be implemented without breaking binary compatibility
Data classes shouldn’t be used in an API as they aren’t binary-compatiblity safe. https://kotlinlang.org/docs/api-guidelines-backward-compatibility.html#avoid-using-data-classes-in-your-api Having those
val
properties in the class definition (instead of the constructor) makes them constants - may as well move them into a companion object, as they can no longer be externally set.
r
Thanks for the comment! I just updated my question. It doen't need to be a data class.
When I use the below formula in an API, and then move one of the constants to the constructor, so it stops being a constant, it breaks binary compatibility
I'm dealing with UI component descriptors for a BDUI API, and this has ben a huge pain
c
Have you considered making the constructor internal or private, and using factory functions to create instances?
r
I thought that maybe forcing a property in the constructor to be a constant (maybe with the tooling), it could improve my situation
Yes Chris, it would be an option, but this will add lots of boilerplate code
I'm just thinking about an annotation to achieve what I need now
thank you all for your help!
j
I'm sorry but I don't quite get the problem this proposal is trying to solve, nor what you have to write right now to solve it and how it would be made more convenient with this language feature 🤔
Ah, maybe I get it. You want to generally write readonly constants in the constructor, but without allowing to actually pass values in constructor calls, for the purpose of later being able to make them not constant without breaking compatibility. Correct?
I don't understand why not make them regular constructor parameters from the get go then. If your code can handle it anyway, why not?
r
Hi Joffrey, thanks for your message! I'm working on a backend-driven UI library. It includes base components like buttons and thumbnails, and more complex ones like cards that reuse those base components. Some specific variants of the complex components are designed to restrict certain base component properties. For example: An InformativeCard should allow setting the button text, but always use the primary variant of the button. However, these design constraints might evolve over time. For instance, we may later need to allow changing the `buttonVariant`—which is currently a constant. Changing it from a fixed value to a configurable one would break binary compatibility. That’s the kind of scenario I’m trying to handle more gracefully.
j
I see. Then I would suggest using builders instead. The different properties that you expect to be set should be part of the builder interface, you can add more later, you can even deprecate some with
ReplaceWith
clauses etc. It gives you way better evolution possibilities. I hit these compatibility problems in my STOMP client library for the header classes, and solved it by forcing users to use builders instead. I didn't want the "fluent" Java-style builders, though. Instead, I provided some sort of constructor-looking factory function that takes a lambda, and the receiver in that lambda is a builder interface that has `var`s for things I want to allow to change. See this example (and other header classes in the same dir) to see what I mean: https://github.com/joffrey-bion/krossbow/blob/main/krossbow-stomp-core/src/commonMain/kotlin/org/hildan/krossbow/stomp/headers/StompConnectedHeaders.kt
r
Thanks, Joffrey! I’ll definitely take a look at it—really appreciate you sharing the example! 🙌
fist bump 1