Repository navigation
Assign-Once Properties #471
Replies: 9 comments 31 replies
|
To answer your questions:
|
|
About stability: I understand not wanting to open the can of worms that would be a general way to declare delegates stable here. However, having some way to declare things stable has came up multiple different times in different areas (i.e. here, the sharing keep), and I would hope that some solution that works for both delegates, custom getters, and external modules can be created. It seems like a good place for a new contract to me, if delegates could have property contracts. I have written a few stable delegates (e.g. getting from an immutable map, or env var on JVM) before and being able to smart cast them would be super nice. |
I really dislike this. My next preference is to mark |
|
About the allocation of a delegate: I wonder what the future holds in this aspect w.r.t. value classes. If we had |
That's exactly what contracts are for! Contracts let you do way more dangerous things! Your analysis of how difficult it'd be to debug is already true of contracts, which is an incredible feature to have for libraries (including the stdlib) You also say that it's not a property of the |
|
I like the delegate approach more, but for the language approach, So there's precedent for assigning to an uninitialised val. I'd also love a delegate version of |
|
Maybe it's just a placeholder name, but I don't find
I'd find a simpler phrase, something like (I don't have an opinion on a modifier vs delegate) |
|
Thanks everyone for the feedback! We see that people are mostly in favor of introducing
To continue the discussion, we would like to draw your attention to the problems with the CustomizationThe // Delegate-first approach allows customization:
var property: String by assignOnce(mode = AssignOnceThreadSafetyMode.NONE)
// lateinit val provides no way to opt out of thread-safety:
lateinit val property: StringCompilation SchemeWe believe that PerformanceWhile reads and writes are expected to be fairly performant on the JVM, compiling lateinit val property: String
// What the compiler generates:
private val property$delegate = AssignOnce<String>() // extra allocation
var property: String
get() = property$delegate.getValue(...)
set(value) = property$delegate.setValue(...)This mismatch between the lightweight syntax and the heavier underlying mechanism could be a source of confusion, especially when migrating from AnnotationsCompilation scheme of // @Inject targets the backing field, this works:
@Inject lateinit var service: Service
// No backing field, explicit target is required:
@set:Inject lateinit val service: ServiceThe two declarations look almost identical, but behave fundamentally differently with respect to annotations. This is especially problematic given that dependency injection is one of the primary use cases motivating the proposal. SerializationIn @Serializable
class Model {
// Works with kotlinx.serialization:
lateinit var field: String
// Treated as transient by default:
lateinit val field: String
}While most intended use cases for assign-once properties involve non-serializable values, this is another case where ConclusionIntroducing We'd love to hear what you think about these trade-offs. |
|
Thank you everyone for the discussion! It informed the design decisions captured in the successor proposal, KEEP-0455. |
Uh oh!
There was an error while loading. Please reload this page.
Discussion for the Assign-Once Properties proposal. The full text is available here.
This proposal introduces assign-once properties to Kotlin.
They are a middle ground between
lateinit varandval:Assign-once properties fit better where currently
lateinit varis used:fields for dependency injection or late setup initialization.
We discuss two possible ways to surface this feature in the language:
a new modifier for
vardeclarations, or delegated properties.We'd greatly appreciate input from the community, especially regarding the following questions:
@set:) for annotations on assign-once properties acceptable?All reactions