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
feat: Field extensions/updates to existing objects #3084
Conversation
``` > { a = 8 in {}}; {a = 8} : {a : Nat} ```
Look: ``` > { a = 8 in {b = 6}}; {a = 8; b = 6} : {a : Nat; b : Nat} ```
67873b0
to
1bc7fe7
Compare
@nomeata and I discussed this: me: Gabor has implemented my suggestion of requiring users to override inherited var fields, preventing aliasing (in a way that we can relax later, sans breaking anyone). For or against? Indifferent? |
Make mutable field aliasing (when extending/merging records) an experimental opt-in feature, and hide it behind a feature flag. When the flag is not supplied, an error is emitted that instructs the user to overwrite all `var` fields from bases explicitly. When `--experimental-field-aliasing` is given no such error gets emitted, and the mutable cells of bases are aliased in the resulting record. See discussion in #3084 why this is a necessity. Also contains a few minor tweaks. Co-authored-by: Claudio Russo <claudio@dfinity.org>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See doc changes and #3417
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But don't forget my suggestion to the doc. You can also put the other wording in an html comment for preservation.
This is now an opt-in experimental feature Co-authored-by: Claudio Russo <claudio@dfinity.org>
Co-authored-by: Claudio Russo <claudio@dfinity.org>
The restriction makes sense to me. Still curious why you had to make this construct so complex, though. What can it express that a simple binary one can not? |
@rossberg a few reasons:
|
@rossberg Isn't the simplest example one where there are two fields that clash and you want to take the first from the left, but the second from the right record? Though I guess you could forget one field using subtyping before doing the merge (or, perhaps better, add a I agree the complexity is a bit high, especially when you try to describe it in prose.... The binary version isn't that much better in the sense that it still breaks subject reduction and can (equally) only be described as a type based elaboration. |
True, although you can achieve that with a type constraint or more orthogonal means, as you say. In general, bidirectional merging is only practically useful if you have actual mixins (records with abstract fields), short of that, it may not justify the extra complexity. @ggreif, the performance and garbage arguments are a bit unconvincing. It should be fairly easy for a compiler to syntactically recognise the cases that correspond to the complex construct and compile them the same way. As for syntax and symmetry, I'd have thought that the obvious incoherence between |
I am confused to still see IR changes here. I thought the final design did not introduce aliasing of mutable fields? I understand that maybe we’ll get some form of aliasing in the future, but until then it’s dead code and hence dangerous. (unless I am missing something). Maybe revert it in a separate PR, where we can easily take it from if we need it again? |
@nomeata The aliasing feature is still available, but behind an opt-in flag |
Related to #3072.
Functional record updates
This adds the ability to overwrite a set of fields (in a potentially type-changing manner) relative to a (set of) base(s).
The syntax is
The resulting object has type
{ a : val_ty; <more-fields> } and base_1_ty and <more-bases_ty>
as long as the bases and the new fields are field-disjoint. In general the overriding fields are removed from the bases and the resulting type is formed from the overwritten fields' types plus the rump bases' field types.The full syntax is a Swiss army knife of thing, allowing
--experimental-field-aliasing
) ability to create entangledvar
fields (internally: aliased mutable cells).Some fine points
{ base_1 and base_2 and base_3 }
syntax is object concatenation, analogous to the type-leveland
operator.and
operator (when surrounded by braces) puts the bases into object context. This is a different flavour of product construction from theBool
(monoidal) operation (also a product).--experimental-field-aliasing
must be specified] All non-overwrittenvar
fields from bases will show up in the resulting object asvar
fields that alias the original field from its base. This behaviour can be disabled by adding an overwritebase with var field = base.field
. Note that this is analogous to the aliasing behaviour of shared[var]
arrays.TODOs
{ in {} }
in the parser? FIXED: 47ebf5a{ a = 25; in { b = 42 } }
? Yes, FIXED: fbf5adeIN base=exp(ob)
is too permitting (future?) — FIXED: 3120aa1var
field aliasing (works in interpreter)module { M and N };
whereM
,N
are modulesawait
Type.as_obj
crashes on bases #3405)Text
etc. (the special member hacks) — See https://github.com/dfinity/motoko/pull/3084/files#r947909810, howeverRefD
to prim +VarD
— write issue!Changelog.md
{ t : type } and { t : Val }
— what to do? They live in different namespaces!