digitalmars.dip.ideas - assigning to multiple fields in a struct
- Lukanian (77/77) Jun 05 I would like to simplify a situation when i need to change values
- monkyyy (20/97) Jun 05 ```d
- Basile B. (15/21) Jun 06 Builtin tuples + syntax sugar + AST lowering could help here.
- Dejan Lekic (15/17) Jun 06 So you are not happy with:
- Lukanian (14/31) Jun 07 Thank you for reminding me about "with" I almost forgot about it.
- kinke (29/29) Jun 07 This is also an interesting generic approach:
I would like to simplify a situation when i need to change values of some fields of my struct but keep other fields unchanged. Standard situation looks like this. ```d struct MyStruct { int field1; double field2; string field3; int field4; string field5; } MyStruct myStruct = //... // i need to change some values myStruct.field1 = 10; myStruct.field2 = 0.5; myStruct.field5 = "hello"; // and it gets even uglier if struct is inside some complex structure i.e.: myArray[0].someOtherStruct.myStruct.field1 = 10; myArray[0].someOtherStruct.myStruct.field2 = 0.5; myArray[0].someOtherStruct.myStruct.field5 = "hello"; ``` I propose two solutions: - 1. automatically generate setter for every field which returns reference to the struct - 2. automatically generate an assign function with all fields as optional params, but assign only those which are given by user (no idea how to implement this) My attempt to implement solution 1. ```d myStruct .setfield1(10) .setfield2(0.5) .setfield5("hello"); // or just make it in one line myStruct.setfield1(10).setfield2(0.5).setfield5("hello"); ``` I tried to implement it with mixin template: ```d mixin template GenerateSetters(T) { enum fieldNames = FieldNameTuple!T; alias fieldTypes = FieldTypeTuple!T; enum typeName = fullyQualifiedName!T; string generateFunctions() { string param; string fuctions; foreach (i, fieldName; fieldNames) { param = fieldTypes[i].stringof ~ " " ~ fieldName; fuctions ~="ref "~typeName~" set"~fieldName~"(return ref "~typeName~" a, " ~ param ~ ") {\n"~ " a."~fieldName~" = "~fieldName~";\n"~ " return a;\n"~ "}\n"; } return fuctions; } //pragma(msg, generateFunction()); mixin(generateFunctions()); } struct MyStructTest { int field1; double field2; string field3; string field4; string field5; } mixin GenerateSetters!(MyStructTest); // you need to call this after struct definition ``` It works, but if it was built into the language it could be more elegant than this. The **second solution** could look like this, but i don't have an implementation: ```d myStruct.setFields(field1:10, field2:0.5, field5:"hello"); ```
Jun 05
On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:I would like to simplify a situation when i need to change values of some fields of my struct but keep other fields unchanged. Standard situation looks like this. ```d struct MyStruct { int field1; double field2; string field3; int field4; string field5; } MyStruct myStruct = //... // i need to change some values myStruct.field1 = 10; myStruct.field2 = 0.5; myStruct.field5 = "hello"; // and it gets even uglier if struct is inside some complex structure i.e.: myArray[0].someOtherStruct.myStruct.field1 = 10; myArray[0].someOtherStruct.myStruct.field2 = 0.5; myArray[0].someOtherStruct.myStruct.field5 = "hello"; ``` I propose two solutions: - 1. automatically generate setter for every field which returns reference to the struct - 2. automatically generate an assign function with all fields as optional params, but assign only those which are given by user (no idea how to implement this) My attempt to implement solution 1. ```d myStruct .setfield1(10) .setfield2(0.5) .setfield5("hello"); // or just make it in one line myStruct.setfield1(10).setfield2(0.5).setfield5("hello"); ``` I tried to implement it with mixin template: ```d mixin template GenerateSetters(T) { enum fieldNames = FieldNameTuple!T; alias fieldTypes = FieldTypeTuple!T; enum typeName = fullyQualifiedName!T; string generateFunctions() { string param; string fuctions; foreach (i, fieldName; fieldNames) { param = fieldTypes[i].stringof ~ " " ~ fieldName; fuctions ~="ref "~typeName~" set"~fieldName~"(return ref "~typeName~" a, " ~ param ~ ") {\n"~ " a."~fieldName~" = "~fieldName~";\n"~ " return a;\n"~ "}\n"; } return fuctions; } //pragma(msg, generateFunction()); mixin(generateFunctions()); } struct MyStructTest { int field1; double field2; string field3; string field4; string field5; } mixin GenerateSetters!(MyStructTest); // you need to call this after struct definition ``` It works, but if it was built into the language it could be more elegant than this.```d struct MyStruct { int field1; double field2; string field3; int field4; string field5; } unittest{ MyStruct myStruct; with(myStruct){ field1=1; field2=3.14; field3="foo"; }} ``` you will likely be told to do thisThe **second solution** could look like this, but i don't have an implementation: ```d myStruct.setFields(field1:10, field2:0.5, field5:"hello"); ```disagree that this is possible, templated name arguments doesnt have the tools yet
Jun 05
On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:[...] The **second solution** could look like this, but i don't have an implementation: ```d myStruct.setFields(field1:10, field2:0.5, field5:"hello"); ```Builtin tuples + syntax sugar + AST lowering could help here. The sugar in the grammar is ```ebnf DotTupleExp ::= Expression "." TupleExpression ``` for example ```d a.(member1, member2) = (0,1); ``` would be lowered to ```d auto __noSideEffect = a; (__noSideEffect.member1, __noSideEffect.member2) = (0,1); ```
Jun 06
On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:I would like to simplify a situation when i need to change values of some fields of my struct but keep other fieldsSo you are not happy with: ```d MyStruct myStruct = //... // i need to change some values with (myStruct) { field1 = 10; field2 = 0.5; field5 = "hello"; } ``` Right? Is this not simple enough for you? If it is so, then the only solution I can think of is to implement your own multi-setter method. People keep forgetting the with statement and come up with fluent APIs as solution, sigh...
Jun 06
On Friday, 6 June 2025 at 16:15:36 UTC, Dejan Lekic wrote:On Thursday, 5 June 2025 at 21:24:31 UTC, Lukanian wrote:Thank you for reminding me about "with" I almost forgot about it. It may be handy sometimes, but I still think that my version can have some semantic advantages, mainly because it can be used as a rvalue: ```d // passing result to a funcion myFunction(myStruct.setf1(1).setf3(3)); // or myFunction(myStruct.setFields(f1:1, f3:3)); // using it in a chain with oter calls myStructmyStruct.setf1(1).setf3(3).calculateProduct().writeln(); myStruct.setFields(f1:1, f3:3).calculateProduct().writeln(); ```I would like to simplify a situation when i need to change values of some fields of my struct but keep other fieldsSo you are not happy with: ```d MyStruct myStruct = //... // i need to change some values with (myStruct) { field1 = 10; field2 = 0.5; field5 = "hello"; } ``` Right? Is this not simple enough for you? If it is so, then the only solution I can think of is to implement your own multi-setter method. People keep forgetting the with statement and come up with fluent APIs as solution, sigh...
Jun 07
This is also an interesting generic approach: https://github.com/dlang/dmd/blob/8762d1eaee42119269b82a1b1b7063c89c1e6a69/compiler/src/build.d#L2195-L2205 Sketch: ```D struct Builder(T) { T obj; ref Builder opDispatch(string name)(typeof(__traits(getMember, T, name)) arg) { __traits(getMember, obj, name) = arg; return this; } } struct S { int x; double y; string z; } void main() { const s = Builder!S() .x(1) .y(2.0) .z("z") .obj; assert(s == S(1, 2.0, "z")); } ```
Jun 07