| 05:40 | <Mathieu Hofman> | I think I caught up on the discussion.
|
| 09:16 | <shu> | (5) is a non-starter |
| 09:17 | <shu> | it's not possible to change from writable->non-writable for shared structs fields, because the invariant is shared structs have fixed shape |
| 09:17 | <shu> | freezing the properties changes the shape, which requires synchronization, which means all accesses will need to become synchronized on the shape, which is too slow |
| 09:19 | <shu> | the only things i can imagine working is something like being able to freeze the properties before an instance escapes the local thread, but the precise form of that check is also too expensive to perform, so it'll be a conservative check like "has this instance ever been assigned to another shared struct, or been postMessaged" |
| 09:19 | <shu> | i am not sure how useful that is |
| 09:20 | <shu> | the more sensible thing is to declare fields as non-writable and create them non-writable |
| 09:23 | <shu> | (5.a) is also not true, you cannot share a struct before init completes |
| 09:24 | <shu> | but it may be because we have different models of "init" here |
| 09:24 | <shu> | for the same reason of the shape itself being immutable, it won't be possible to do any freezing post-construction |
| 09:25 | <shu> | so the only way is to declare the shape up front to have some field f be already frozen, and there would be some generated constructor that takes the initial value for f such that by the time user code gets a constructed instance, it has the value for f already. the user initializer won't be able to change the value of the field |
| 09:26 | <shu> | i'd really like to defer declaration of frozen fields and to be a follow-on proposal if possible |
| 09:28 | <Mathieu Hofman> | My understanding of structs was that the object is constructed with a known set of fields with each a value of undefined, then the init step runs, which can set these fields to their value. |
| 09:28 | <shu> | freezing the properties changes the shape, which requires synchronization, which means all accesses will need to become synchronized on the shape, which is too slow |
| 09:28 | <shu> | My understanding of structs was that the object is constructed with a known set of fields with each a value of undefined, then the init step runs, which can set these fields to their value. |
| 09:29 | <shu> | so your (5) can't come up in the current proposal, is what i was explaining |
| 09:29 | <Mathieu Hofman> | That init step could share or otherwise set the struct as a field of another struct, which means it can escape before all the init steps complete |
| 09:29 | <shu> | "non-applicable" would've been better than "non-starter" |
| 09:29 | <shu> | That init step could share or otherwise set the struct as a field of another struct, which means it can escape before all the init steps complete |
| 09:33 | <Mathieu Hofman> | Yeah I just don't see in this construction+init model how you could provide a value for a field before the instance is constructed, without reverting to the model classes have, aka have a dead zone before super is called. |
| 09:34 | <shu> | oh it'd probably be some ugly thing |
| 09:35 | <shu> | but yeah i haven't fully thought out how this would look |
| 09:36 | <shu> | you can imagine something like, the first argument to a shared struct constructor is always an "initializer object" whose fields get assigned to like-named fields on the shared struct, before the user initializer is called |
| 09:36 | <shu> | this is real ugly because if your user initializer takes arguments you'd always be doing new MyStruct(undefined, myFirstArg, etc) |
| 09:37 | <Mathieu Hofman> | Well I suppose only the base constructor needs that argument |
| 09:38 | <shu> | well, the subclasses need to pass it along somehow |
| 09:38 | <Mathieu Hofman> | Oh right. Ugh that's not ergonomic |
| 09:39 | <shu> | nope, "some ugly thing" |
| 09:39 | <shu> | which is partly why i'd like to avoid speccing frozen-at-declaration fields initially |
| 09:41 | <Mathieu Hofman> | Should probably think it through to make sure the init mechanism doesn't make that impossible in the future |
| 09:43 | <Mathieu Hofman> | The way we've been handling a similar problem currently is to have our "init" return the set of fields and not have access to the instance reference (which doesn't actually get created until after init runs) |
| 09:45 | <Mathieu Hofman> | Then we have an optional "finalize" step which gets access to the populated instance and gets to perform any external wiring before the instance is returned to the caller |
| 11:32 | <littledan> | We would really need C++-style initializer lists to do this kind of frozen property well, IMO |
| 11:33 | <littledan> | or, we could go back to Records and Tuples, but object-based -- the various ways of constructing them let you fill in contents without modifying existing things |
| 11:34 | <littledan> | ES6 classes didn't really give us a great basis for initializer lists because of how the instance is constructed in the base class and then subsequent subclass constructors can just mutate it. This constrained the design of class fields a lot |
| 11:56 | <shu> | We would really need C++-style initializer lists to do this kind of frozen property well, IMO |