Skip to content

Latest commit

 

History

History
138 lines (92 loc) · 4.13 KB

FS-1049-nested-record-field--copy-and-update-expression.md

File metadata and controls

138 lines (92 loc) · 4.13 KB

F# RFC FS-1049 - Nested Record Field Copy and Update Expression

The design suggestion Support first-class lensing / lenses This RFC covers the detailed proposal for this suggestion.

Summary

Enable updating nested record field with "with" syntax.

Motivation

This improves readability in cases where nested record field values need to be updated.

For example, take the following code:

type Street = { N: string }
type Address = { S: Street }
type Person = { A: Address; Age: int }

let person = { A = { S = { N = "Street 1" } }; Age = 30 }

let anotherPerson = { person with A = { person.A with S = { person.A.S with N = person.A.S.N + ", k.2" } } }

let anotherPerson1 = { person with A.S.N = person.A.S.N + ", k.2" }

Detailed design

Transform the AST in TypeChecker.fs to expand the new syntax into the current form.

let anotherPerson = { person with A.S.N = person.A.S.N + ", k.2" }

Becomes:

let anotherPerson = { person with A = { person.A with S = { person.A.S with N = person.A.S.N + ", k.2" } } }

Need to consider:

  1. Handle the possible ambiguity of specifying type name vs field name
  2. Group nested field from the same parent
  3. All nested field parts need to be declared on record type
  4. Collaborate with anonymous records feature
  5. Check IntelliSense
  6. Investigate other language features that should support nested paths like named arguments
  7. Check same field is not updated twice within statement

Syntax

This change allows updating nested fields, using dot notation, within one copy and update statement.

let anotherPerson = { person with A.S.N = "1" }

Multiple fields with differing levels of nesting can be updated within the same expersssion.

let anotherPerson = { person with A.S.N = "1"; Age = 1; }

Fields can be accessed through Namespace, Module or Type name.

The implementation expands the nested syntax into the existing AST for nested updates so qualified access is checked through the same mechanism.

TypeName Access

let anotherPerson = { Person.A.S.N = "1"; Person.Age = 1; }

ModuleOrNamespaceName Access

let anotherPerson = { ModuleOrNamespaceName.A.S.N = "1"}

ModuleOrNamespaceName.TypeName Access

let anotherPerson = { ModuleOrNamespaceName.Person.A.S.N = "1" }

Drawbacks

Additional complexity in the compiler.

Compatibility

This is not a breaking change, and is backwards-compatible with existing code.

Open Questions

Q: What about indexers, e.g.

let anotherPerson1 = { person with A.[3].N = person.A.[3].N + ", k.2" }

A: Need to look at it

Q: What happens to cases where A.S is mentioned twice?

 { person with A.S.N = person.A.S.N + ", k.2"; A.S.M = person.A.S.M + ", k.3"  }

A: Already implemented will compile to

 { person with A = { person.A with S = { person.A.S with N = person.A.S.N + ", k.2"; M = person.A.S.M + ", k.3"  }

Q: What about similar features in the language that name fields, especially mutating property setters

Person(A.C.N = 3, A.C.M = 4)

A: Need to look at it after we finish with the records

Q: Will it work with struct records without introducing lot's of struct copy?

A: The current implementation is expanding the simplified AST into the existing one and will have the same struct copy issue mentioned by @zpodlovics. However, as this issue is present in the original copy and update expression this would have to be fixed as well as updating the new implementation. This will be investigated further once the open tasks are completed.

Alternatives

  • Do not implement this feature