Search code examples
f#anonymous-types

Generically add field to anonymous record using a function


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#?


Solution

  • 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.