Discussion: "semi-constructed" object state / "Immutable" fields. #2957
Replies: 9 comments
-
I think that there's some interesting ideas here. However this is all coming from the assumption that an object is not fully constructed till after the method it's constructor is called in. This works really well for factory methods. However another far more common pattern is to create an object and then use it in the same method: M()
{
var x = new ImmutableClass();
UseX(x);
x.readonlyField = 42; // oops, this just changed
} How would you deal with that? |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
@YairHalberstadt there are several answers to that depending on your point of view, some thoughts i had on directions here are that:
@ufcpp the initonly part of the records proposal only allows the semi-constructed state to extend to the end of the object initializer, which doesn't allow sophisticated function calls or logic to be applied in the construction. |
Beta Was this translation helpful? Give feedback.
-
It feels like this could all be designed around the same concept c# has for ensuring all the fields of a struct are assigned before the struct instance itself is considered fully assigned. For classes we'd naught just need to Mark the fields and props that would have to be assigned post construction anda similar analysis would be performed. |
Beta Was this translation helpful? Give feedback.
-
Sounds like #2328. 😏 |
Beta Was this translation helpful? Give feedback.
-
@Joe4evr , @CyrusNajmabadi those cases still have the "so when is this object considered complete" -- developers still need a way to say when they want the object to transition to being truly immutable, eg: the struct initializer proposal linked doesn't allow child function calls the possibility to mutate the late-init fields. |
Beta Was this translation helpful? Give feedback.
-
I like kotlin's class Form1 : Form {
public lateinit string Prop { get; }
public Form1() {
Load += Form1_Load;
}
void Form1_Load(object sender, EventArg e) {
Prop = "This property was initialized in Form1_Load";
}
} |
Beta Was this translation helpful? Give feedback.
-
This is similar to some of the thinking around required initialization. I don't care for it for a multitude of reasons, mainly around the "weakening" of |
Beta Was this translation helpful? Give feedback.
-
@ronnygunawan The problem with Kotlin's i.e. the following code produces no errors or warnings: public class MyTest {
lateinit var s: String
public val subject = s
}
fun main() {
var m = MyTest()
println(m.subject)
} It just fails at runtime with an exception, making it little better than null. |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Problem statement:
Immutables (#421,#2543,#2443), Records, friend types, discussions about primary constructors and how to add new members to types, nullability handling, and other discussions about constant-ish alterations to the language seem to get stuck around "how to handle deferred construction" .
I believe the problem is that there is a significant difference between when the language considers and object to have been constructed (which is closely tied to memory allocation, and the completion of the "new" function call) and that developers have objects that they consider to be semi-constructed objects.
For example during serialization where reflection is used to poke 'readonly' things which are in a Constructed() but not constructed (ie, properly initialized fields) state, and the function responsible for initialization wants to be able to violate "private/readonly-ness" of an object before returning it for consumption/and sharing .
Proposal
Example:
I believe that there would be many optimisations possible through recursively discoverable immutable values at runtime by the jit (or AOT) - when an inner func is JIT'd it may be possible that an immutable return value is discovered. In which case certain inlinings not currently allowed would be.
Enforcement of semi-constructed objects:
a) compile time:
b) runtime:
Implementation Considerations:
-forwards compatability
this proposal as described doesn't require assistance from the runtime, so is fully forwards compatable.
adding the first immutable field to a type is potentially a binary breaking change, if the runtime implementation introduces a new boolean field, and there isn't a bit spare somewhere in the object definition to handle it
Thought: is it possible to have the value of boolean annotation altered by the runtime? -- this would be the first time an annotation wasn't a const value
when could an object be considered complete
if a developer want an object to be considered "complete" part the way through a function call (as mentioned by @YairHalberstadt below
a) i can tell this might be a bug as x isn't returned, for non-returned immutables, it is an error to mutate the readonly:
ai) without declaring where the scope for which construction completes.
aii) after the first time it is used as a function parameter?
aiiI) after the last time it is used as a function parameter?
b) perhaps an informational "late assignment of readonly field at x.readonly=42; to assist with diagnoses of bugs. (enable "pit of success, at cost of slightly annoying people who use this feature?)
a) construction is complete on assignment to another variable (eg, var useX();var y=x; the object is complete;
b) construction is complete on leaving a scope that that isn't a branching or loop operator (ie, manual { } scopes, using statements, ...
c) construction is complete upon returning from a function which uses it as a parameter? (but a factory might want to do multiple things (populate name, lookup address, apply record security...) )
a) x! as a statement.
b) new! as an operator to declare that partial construction is complete.
Code example that got me thinking about some of the above, based on the thought "couldn't we just inline the readonly"
this is "safe" code, but very much not actually safe for anyone who assumes that the readonly things don't change... It'd be nice for a runtime guarantee to guard that, not just a compile time declaration of intent.
Beta Was this translation helpful? Give feedback.
All reactions