I would like to be able to take an arbitrary record as a parameter and return an anonymous record with a field added using the copy-and-update syntax.
For example, this works:
let fooBar =
{| Foo = ()
Bar = () |}
let fooBarBaz = {| fooBar with Baz = () |}
But I would like to do this:
let fooBar =
{| Foo = ()
Bar = () |}
let inline addBaz a = {| a with Baz = () |} (* The input to a copy-and-update expression that creates an anonymous record must be either an anonymous record or a record *)
let fooBarBaz = addBaz fooBar
Is there a way to do this in F#?
No, that's not possible.
Think about it, if that function was possible, what would be the type ? Is it something that "fits" into existing F# type system?
The type should be something like val addField: x: {| FieldName<1>: 't1; FieldName<2>: 't2; ... FieldName<n>: 'tn;|} -> {| FieldName<1>: 't1; FieldName<2>: 't2; ... FieldName<n>: 'tn; Baz: unit |}
Clearly something like that's not representable in F# type system.
UPDATE
It was mentioned that adding a record constraint would allow this but this is quite far from reality. A record constraint would just filter the argument to be a record but the problem which remains is how do the type system express that the function takes a type ^T when ^T : record
and returns something like ^U when ^U : ^T_butWithAnAdditionalField
Also, on the SRTP side there is a way to "read" a field by adding a get_FieldName
constraint but not to write, moreover the possibility to read a field allow us to read a field of a known name, not any name.
Conclusion: F# type system is very far away from allowing to express stuff like this and the SRTP mechanism is not there either.
Allowing an "is record" constraint shouldn't be that complicated but it won't solve anything here.