-
-
Notifications
You must be signed in to change notification settings - Fork 98
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
[Revived] In-place struct initialization #71
Conversation
DIPs/DIP1xxx-sw.md
Outdated
|
|
||
| Example: | ||
|
|
||
| auto s = S({a:42, b:-5}); |
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.
In my opinion this looks like passing a single value to the constructor of S. Perhaps I'm just used to Ruby, where the above is an associative array.
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.
Hmm I just realized that this is ambiguous with anonoymous FunctionLiterals:
AST.Expression parsePrimaryExp()
{
...
case TOKlcurly:
case TOKfunction:
case TOKdelegate:
case_delegate:
{
AST.Dsymbol s = parseFunctionLiteral();
e = new AST.FuncExp(loc, s);
break;
}[Source](https://github.com/dlang/dmd/blob/master/src/ddmd/parse.d#L7559
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.
No, the only ambiguous case is {}, and it can be arbitrarily resolved to a function literal to keep backwards compatibility. Non-empty function literals (with braces) always contain statements, which either end in a semicolon, or contain a keyword (e.g. struct).
DIPs/DIP1xxx-sw.md
Outdated
|
|
||
| auto s = S([a:42, b:-5]); | ||
|
|
||
| This syntax may potentially be ambiguous with associative arrays. |
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.
I'm pretty sure this is true. Not sure if it's worth having as a proposal at all.
|
|
||
| Example: | ||
|
|
||
| s = S(c:10, b:20); |
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.
In my opinion this is the best syntax.
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.
This would work if D allowed named arguments.
|
I think the focus should be on the inconsistency in the language, that is, that there's this very specific syntax |
DIPs/DIP1xxx-sw.md
Outdated
|
|
||
| To understand the proposed changes in a short recap the interesting parts of | ||
| D's grammar will be highlighted. | ||
| Currently [static struct initialization](http://dlang.org/spec/grammar.html#StuctInitializer) |
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.
typo: #StructInitializer
DIPs/DIP1xxx-sw.md
Outdated
| assert(args!(fun, b=>3) == 16); | ||
| ``` | ||
|
|
||
| While this is very nice trick, it has a few downsides: |
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.
I think a major reason is that it relies on function parameter names not changing, i.e. they become part of the library API. Named arguments need to be distinguished from normal parameters, which can be renamed - this DIP solves this problem.
DIPs/DIP1xxx-sw.md
Outdated
| .open; | ||
| ``` | ||
|
|
||
| Even though this is a nice Java style, allocating a class and function calls don't come for free. |
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.
Nitpick: sounds like a struct would be better rather than class for stack allocation. I don't know but maybe the optimizer can reduce the function calls to the equivalent of struct initialization. But one problem with this pattern is that it's a bit tedious to write those methods by hand.
DIPs/DIP1xxx-sw.md
Outdated
| aes!("x", "y")(dat1.value1, dat1.value2) | ||
| ``` | ||
|
|
||
| #### Vulkan |
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.
This heading and the next two should use ### for level-3, they're not part of Plotting.
DIPs/DIP1xxx-sw.md
Outdated
| ### Links | ||
|
|
||
| - [D grammar](https://dlang.org/spec/grammar.html) | ||
| - [Language specs on structs](https://dlang.org/spec/struct.html) |
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.
Maybe change this (or add a link) to static struct initialization spec
|
My experience writing #66 gave me some insight into how a persuasive DIP runs in the general case. I'm copying this comment I made for #61 , as it applies here too. My preferred order for DIP presentation is: Section: Rationale (i.e. State the Problem) Section: Description Section: Drawbacks and Alternatives (drawbacks, i.e. breakage, language complexity, corner cases) |
|
I think the grammar descriptions and AST sections are particularly unpersuasive. The grammar should be moved to the bottom of the DIP, and only kept because it's necessary for a complete proposal. But I think the AST sections can be completely removed. I really can't imagine anyone being persuaded by them. If there are any parsing problems, they should be addressed somewhere else. |
|
@jacob-carlborg @ntrel @zachthemystic thanks a lot for your helpful feedback - I am just trying my best at reviving the best, so please have a bit of patience.
Fair point - I tried to reword it, but it still needs more. Feel free to point out sections that you thought weren't helpful. @zachthemystic: thanks a lot for the idea - I restructured the page and put the rationale after the description.
Except for the workarounds with named arguments, I couldn't a reason not to allow in-place struct initialization anywhere. Ideas?
Thanks - what in your opinion could I add to help in convincing a reader?
Hmm I found it very helpful to understand the consequences of the grammar changes - what do other people think on this? |
|
My first approach is as a general reader. I think the technical decisions can be very difficult, as there are many pros and cons, which must be weighed very accurately. So I'm starting by ignoring the technical details, trying to assess the quality of the writing for its persuasiveness. The following are my opinions, and I don't consider them absolutely right or wrong. The Abstract should be punchier, starting with the 2nd sentence. For example: "This DIP proposes to expand support for static struct initialization to any place where calling a struct constructor would be possible." Imagine you only have one sentence to grab the reader's attention. If you absolutely need more sentences, then okay, but the first one needs to say concisely and in active terms what the DIP does. The Links section is okay, not because it grabs, but simply because it's necessary. So then I get to Description, and I'm already kind of losing interest. I need to be convinced that there's a problem. Imagine the reader has a thousand other things to do than read the DIP (which is probably true). What can you say to convince them there is an important problem here that is worth their attention? That is why I would rather read the rationale at this point. Only when I'm convinced there's a problem do I need to see the solution. The existing Rationale which follows does not actually do this, in the sense that it promotes benefits without making the problem felt. I realize that none of what I'm saying actually addresses technical concerns. But I have enough confidence that whoever reads this, including the language authors, are human beings, who are always affected by many things other than pure logic, which is why I'm saying it. I'll continue to review if and when the above comments are addressed. |
|
the work on this dip is highly appreciated. For my AWS SDK this DIP would I generate structures out of the AWS API information. Several UDA information has to be stored. Struct initializer for UDA structures will look great: Second scenario is the actual usage of these structs. Using struct initializer in method signature feels natural: |
|
@wilzbach I'm going to agree with @jacob-carlborg about focus. As it reads now, it sounds more like a DIP for named arguments than for struct initialization. IMO, consistency is the key selling point here, as is the principle of least surprise; if it works in declarations, it's reasonable to expect that it should work elsewhere and surprising that it doesn't. And if memory serves, Walter is against named arguments (there have been multiple discussions in the forums), so repeating the phrase so frequently may actually be counter-productive! |
|
It's a nice DIP IMO. I sometimes have the case of defavoring struct constructor syntax in favor of field by field assignment for the sake of readability. Consistency with the static assignment syntax would be to accept this very syntax everywhere a struct can be initialized: struct S {
uint a;
long b;
int c;
}
struct SS {
uint d;
S s;
}
void foo (int e, S s);
void bar (SS ss, int f);
S s = { a: 42, b: -5 }; // c initialized as int.init
SS ss = { s: { a: 42, b: -5 } };
foo(12, { a: 42, b: -5 });
bar({ s: { a: 42, b: -5 } }, 54);
// without forgetting default arguments
// following a and b known at compile time, previous ones can be evaluated at run time
void baz (int g, S s = { a: 42, b: -5 });How about the case of a template argument? It should be possible to optionally specify the type: void tfoo(T)(int e, T s);
tfoo(12, S { a: 42, b: -5 });The In that case, 2 options:
I prefer the latter:
|
|
Here's an idea to get around having either named argument-syntax for this purpose or something that conflicts with There was an old proposal that I faintly remember about having So we could then have: auto my_anon_struct = {foo: 42, bar: 89}; // type == Tuple!(int, "foo", int, "bar")
// or something like it
auto my_var = {foo: 42, bar: 89}.as!MyStruct; // type == MyStruct
pragma(inline, true)
auto as(T, U)(auto ref U u) {
T t = mixin(getTupleLiteral!(U, "u"));
// This gives us something like:
// T t = {foo: u.foo, bar: u.bar};
// Pretty straightforward so hopefully the compiler can
// reliably inline
return t;
} |
|
Might be of interest in relation to this 'anonymous records as field-named tuples' suggestion: https://www.microsoft.com/en-us/research/wp-content/uploads/1999/01/recpro.pdf I don't mean to sideline the suggestions laid out in this proposal. This's merely the only place I know of right now that has an ongoing discussion about it and I'm not sure about starting an NG thread for this while a proposal is alive. |
|
Found this thread on the NG: http://forum.dlang.org/post/ok9nt7$2e5d$1@digitalmars.com @ others thanks a lot for your feedback. Highly appreciated, but unfortunately I won't have much time for this DIP this month, so if you want to push it feel free to open PRs against my branch. |
| return s; | ||
| } | ||
| playWithS(createS(b=3)); | ||
| ``` |
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.
no syntax highlighting
|
In the State of D survey, in-place struct initialization was the fourth-most missed language feature with 28% of all respondents missing it: |
So what are we waiting for 😃. |
|
Yeah, what are you waiting for? |
|
I think the main blocker is the lack of consensus on a syntax. Not sure how to go about it, people can just shout what they think is the best idea but it won't amount to much, and bringing that issue to the forums would be... let's call that a risky bet. Some kind of structured poll maybe? |
5d2a21a
to
4c254d7
Compare
Well I was quite busy and somehow no one else pushed this further :/
AFAICT
So chances are that we will end up with Option 1, but I expected this issue to be discussed in-depth in the first review stage of this DIP. |
|
Option 1 could just as well be in conflict with tuples with names. |
| AttributeDefinitions attributeDefinitions; | ||
| } | ||
| ``` | ||
|
|
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.
I think another great example would be to create a struct instance to be converted to JSON in an implementation of a HTTP API endpoint.
|
BTW, C++11 supports this, using the following syntax: struct Foo
{
int a;
int b;
};
auto a = Foo{ .b = 4 }; |
4c254d7
to
bf94283
Compare
bf94283
to
26c7274
Compare
-> dlang/dmd#8460
Does this also apply to Option 1? (the default option of this DIP and the option used for the PoC DMD PR). @mdparker is there a slot in the DIP queue available at the moment and what do you think still needs to be done for an initial submission? |
26c7274
to
baae3e4
Compare
|
@wilzbach looks like my analysis was incorrect. This statement was less than adequately explained in examples I think " Of course my immediate assumption that 1 and 2 would have some sort of function calling behavior applied to it, is concerning. |
baae3e4
to
927864b
Compare
Initialization. FWIW with dlang/dmd#8460 struct S
{
int a = 2, b = 4, c = 6;
}
void foo()
{
bar(S({c: 10}));
}
void foo2()
{
S s = {c: 10};
bar(s);
}
void bar(S);0000000000000000 <_D3fooQeFZv>:
0: 55 push %rbp
1: 48 8b ec mov %rsp,%rbp
4: 48 83 ec 10 sub $0x10,%rsp
8: c7 45 f0 02 00 00 00 movl $0x2,-0x10(%rbp)
f: c7 45 f4 04 00 00 00 movl $0x4,-0xc(%rbp)
16: c7 45 f8 0a 00 00 00 movl $0xa,-0x8(%rbp)
1d: 48 8b 55 f8 mov -0x8(%rbp),%rdx
21: 48 8b 7d f0 mov -0x10(%rbp),%rdi
25: 48 89 d6 mov %rdx,%rsi
28: e8 00 00 00 00 callq 2d <_D3fooQeFZv+0x2d>
2d: c9 leaveq
2e: c3 retq |
|
Regarding the third option is ambiguous with token strings. We already have the problem with attributes and UDAs. That is, I can declare a struct with the name |
927864b
to
b1283b4
Compare
|
One question: since we're talking about initialization and not construction, should the following be legal (assuming syntax option 1)? If not, how should that be justified? If so, should there be any form of restriction or something? |
@cym13 No. Since if you define a constructor the initializer syntax cannot be used: struct Foo
{
int a;
this(int a) {}
}
void main()
{
Foo f = { a: 3 };
} |
|
For reference the most recent discussion on the forum about this DIP was here: https://forum.dlang.org/post/lpixarbirhorkltaqlew@forum.dlang.org |
|
#126 does not conflict with syntax option 2, as I see it. The reason is that that #126 states that named parameters do not affect overloading resolution. This means that a constructor call where all parameters are named has same overloading as a constructor without parameters -and defining one for structs is currently forbidden. No conflict. If this is extended to classes, there will be conflict with default constructors, but I don't see it as a problem because it won't break anything. Just disallow in-place initialization for classes that have an explicit default constructor. |
b1283b4
to
3068c4b
Compare
|
In all 3 options, the name of the struct is mentioned. I wonder wheter in addition it would be possible leave out the struct name in case the compiler could detect there is only 1 distinct structure which fits. For example a function "foo" only accepts structure "A" it would be possible to just write |
|
I just saw, it is already described in "bonus 1" :) |
|
The syntax I'd go with plain It's the option most consistent with the current state: I really dislike the parentheses since it is not a constructor call. If the struct happens to be named
to avoid building a string literal. |
|
There is a proposal from Walter which goes also into this direction Quote: |
|
I just stumbled upon a problem today. I wanted to use UDAs. I choose a struct because I wanted to optionally pass various arguments (mostly int, floats and strings). Lets call it I want to be able to write something like this @(S{group: "G1", min_n:100})
void f(const size_t n, int a, int b) {
}
@S
void g(const size_t n) {
}for example. Right now it is really cumbersome to try and do this. Only option is to try using fluent-like interface to build the struct as an expression, or a lambda: private enum s1 = (delegate S() { S s = {group: "G1", min_n:100}; return s; })();
@s1
void f(const size_t n, int a, int b) {
}Which is ugly. also, I and everybody kind of likes auto s = {group: "G1", min_n:100};with struct initalizers, it would be easy: auto s = S{group: "G1", min_n:100};Another option would be to have default named arguments for functions and methods, and create a factory for these structs (but named arguments would need to use something else than Whatever |
|
Actually, I think this lambda I showed in the example can be trivially generated using some mixin and CTFE and exposed as easy to use template. |
For me this DIP lives or dies on this. Typing a struct name just to pass it directly to a function that only accepts that struct as a parameter gets really old.
This suggestion is great! The That said, who in their right mind would call a struct |
This would almost certainly break some code (e.g. auto-generated code, translated C/C++/... headers) for very little gain. |
|
Unless I'm mistaken, this should be superseded by the named arguments DIP. |

This is a revival of #22, because I thought the idea is cool and I often could profit from the changes of this DIP.
I am not an expert on the D grammar, so @ everyone, please feel to criticize the proposed additions.
CC @cym13 @Enamex
As this is an adopted PR, help to polish it is more than welcome.