digitalmars.D - Proposal: real struct literals
- "Jarrett Billingsley" <kb3ctd2 yahoo.com> Jun 24 2008
- Robert Fraser <fraserofthenight gmail.com> Jun 24 2008
- Mike <vertex gmx.at> Jun 24 2008
- "Jarrett Billingsley" <kb3ctd2 yahoo.com> Jun 24 2008
- "Steven Schveighoffer" <schveiguy yahoo.com> Jun 24 2008
- Bill Baxter <dnewsgroup billbaxter.com> Jun 24 2008
- "Jarrett Billingsley" <kb3ctd2 yahoo.com> Jun 24 2008
- Bill Baxter <dnewsgroup billbaxter.com> Jun 24 2008
- "Jarrett Billingsley" <kb3ctd2 yahoo.com> Jun 24 2008
- Bill Baxter <dnewsgroup billbaxter.com> Jun 24 2008
- downs <default_357-line yahoo.de> Jun 25 2008
The status of struct literals and construction in D1 has always really
grated on me. So I've come up with a possible alternative syntax which
would supplant the current syntax. (I've reproduced this in an enhancement
request in bugzilla.)
The current struct literals use a function-call-looking style to construct
structs. This has some minor issues:
- If you define a static opCall for the struct, even if it isn't supposed to
be used as a "constructor", you can no longer use struct literals on that
struct. I'm not sure if this was an intended aspect of the design, but it
becomes annoying to implement that static opCall without using struct
literals!
- Once structs get real constructors, the opCall "blessing" becomes
superfluous, and I hope plans are in the works to remove it.
- Using a struct literal does not call a function, so it seems weird to use
syntax that looks like a function call to construct it.
The much more serious issue is that in terms of features and syntax, static
struct initialization and struct literals are *completely* different.
struct S
{
int x, y;
char[] s;
}
void foo()
{
static S s = { 5, y: 10, s: "hi!" };
auto s2 = S(5, 10, "hi!");
}
They have completely different syntax and features. Static struct
initializers are far more powerful: you can name members, initialize them
out of order, and skip arbitrary members. The struct literal syntax is far
more limited: you must initialize the members in order (which quickly gets
out of hand for more than 2 or 3 members), you can't name them, and you can
only skip members at the end of the struct.
So I propose that struct "literals" be replaced with actual struct literals,
which look like static struct initializers. There is a very obvious,
simple, unambiguous syntax for this: an identifier, followed by a static
struct initializer. Crazy, I know!
static s = S{ 5, y: 10, s: "hi!" };
auto s2 = S{ 5, y: 10, s: "hi!" };
Now struct literals have all the nice capabilities of static struct
initializers. Static struct initializers actually no longer have to be
special-cased. The declaration of s above expects the initializer to be
evaluatable at compile-time, just like any other static declaration, and so
the struct literal on the RHS now just has to contain all
compile-time-evaluatable values. No problem.
------
Dreaming, this also opens up the possibility for named function parameters.
Consider a function:
struct Args
{
void* dest;
void* src;
size_t num;
}
void memcopy(Args args)
{
// Copy args.num bytes from args.src to args.dest
}
...
memcopy(Args{ dst, src, 8 }); // Use ordered params
memcopy(Args{ src: a, dest: b, num: a.length }); // Use named params
The issue with this is that you have to have a different named struct for
every function that takes this style of parameters. But -- here's the cool
trick -- extend typesafe variadic parameters to take structures like they
already do for classes...
void memcopy(Args args...) // hee hee!
memcopy(dst, src, 8); // woah, looks like a normal function..
memcopy(src: a, dest: b, num: a.length); // bam, named params for free!
Having colons in the parameter list for a function call is also unambiguous.
Jun 24 2008
Jarrett Billingsley Wrote:The status of struct literals and construction in D1 has always really grated on me. So I've come up with a possible alternative syntax which would supplant the current syntax. (I've reproduced this in an enhancement request in bugzilla.) The current struct literals use a function-call-looking style to construct structs. This has some minor issues: - If you define a static opCall for the struct, even if it isn't supposed to be used as a "constructor", you can no longer use struct literals on that struct. I'm not sure if this was an intended aspect of the design, but it becomes annoying to implement that static opCall without using struct literals! - Once structs get real constructors, the opCall "blessing" becomes superfluous, and I hope plans are in the works to remove it. - Using a struct literal does not call a function, so it seems weird to use syntax that looks like a function call to construct it. The much more serious issue is that in terms of features and syntax, static struct initialization and struct literals are *completely* different. struct S { int x, y; char[] s; } void foo() { static S s = { 5, y: 10, s: "hi!" }; auto s2 = S(5, 10, "hi!"); } They have completely different syntax and features. Static struct initializers are far more powerful: you can name members, initialize them out of order, and skip arbitrary members. The struct literal syntax is far more limited: you must initialize the members in order (which quickly gets out of hand for more than 2 or 3 members), you can't name them, and you can only skip members at the end of the struct. So I propose that struct "literals" be replaced with actual struct literals, which look like static struct initializers. There is a very obvious, simple, unambiguous syntax for this: an identifier, followed by a static struct initializer. Crazy, I know! static s = S{ 5, y: 10, s: "hi!" }; auto s2 = S{ 5, y: 10, s: "hi!" }; Now struct literals have all the nice capabilities of static struct initializers. Static struct initializers actually no longer have to be special-cased. The declaration of s above expects the initializer to be evaluatable at compile-time, just like any other static declaration, and so the struct literal on the RHS now just has to contain all compile-time-evaluatable values. No problem. ------ Dreaming, this also opens up the possibility for named function parameters. Consider a function: struct Args { void* dest; void* src; size_t num; } void memcopy(Args args) { // Copy args.num bytes from args.src to args.dest } ... memcopy(Args{ dst, src, 8 }); // Use ordered params memcopy(Args{ src: a, dest: b, num: a.length }); // Use named params The issue with this is that you have to have a different named struct for every function that takes this style of parameters. But -- here's the cool trick -- extend typesafe variadic parameters to take structures like they already do for classes... void memcopy(Args args...) // hee hee! memcopy(dst, src, 8); // woah, looks like a normal function.. memcopy(src: a, dest: b, num: a.length); // bam, named params for free! Having colons in the parameter list for a function call is also unambiguous.
Usually I like to find one thing to argue with in these proposals, but that sounds about perfect. Mad props for typing this all up, though I'm not sure it'll get anywhere. Try Bugzilla, too.
Jun 24 2008
On Tue, 24 Jun 2008 22:30:28 +0200, Jarrett Billingsley <kb3ctd2 yahoo.com> wrote:Dreaming, this also opens up the possibility for named function parameters.
Class literals? -- Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
Jun 24 2008
"Mike" <vertex gmx.at> wrote in message news:op.uc9uhjp1kgfkbn lucia...On Tue, 24 Jun 2008 22:30:28 +0200, Jarrett Billingsley <kb3ctd2 yahoo.com> wrote:Dreaming, this also opens up the possibility for named function parameters.
Class literals?
That, I'm not so sure about. How often do you have public class fields? I mean, I'm sure someone could come up with a use for them, but structs seem much more suited to having literals.
Jun 24 2008
"Jarrett Billingsley" wroteSo I propose that struct "literals" be replaced with actual struct literals, which look like static struct initializers. There is a very obvious, simple, unambiguous syntax for this: an identifier, followed by a static struct initializer. Crazy, I know! static s = S{ 5, y: 10, s: "hi!" }; auto s2 = S{ 5, y: 10, s: "hi!" };
Agree 100% -Steve
Jun 24 2008
Jarrett Billingsley wrote:The status of struct literals and construction in D1 has always really grated on me. So I've come up with a possible alternative syntax which would supplant the current syntax. (I've reproduced this in an enhancement request in bugzilla.) The current struct literals use a function-call-looking style to construct structs. This has some minor issues: - If you define a static opCall for the struct, even if it isn't supposed to be used as a "constructor", you can no longer use struct literals on that struct. I'm not sure if this was an intended aspect of the design, but it becomes annoying to implement that static opCall without using struct literals! - Once structs get real constructors, the opCall "blessing" becomes superfluous, and I hope plans are in the works to remove it.
But there will still be the problem that using call syntax for struct construction gets in the way of using structs as functors. There's no way to distinguish between "I'm constructing" and "I'm calling this as a functor". If you're lucky construction parameters and functor parameters don't overlap, but if you're unlucky you must contort your code. Struct construction should not use function call syntax.- Using a struct literal does not call a function, so it seems weird to use syntax that looks like a function call to construct it. The much more serious issue is that in terms of features and syntax, static struct initialization and struct literals are *completely* different. struct S { int x, y; char[] s; } void foo() { static S s = { 5, y: 10, s: "hi!" }; auto s2 = S(5, 10, "hi!"); } They have completely different syntax and features. Static struct initializers are far more powerful: you can name members, initialize them out of order, and skip arbitrary members.
This is the only place where I disagree with you. Static struct initializers are more powerful in the ways you list, but in other ways they are less powerful. With the static opCall you can make the parameters be whatever you want them to be -- they don't necessarily have to correspond to an actual member of the struct. And you can also put arbitrary extra construction logic inside a static opCall. So I like the idea generally, but I would like it much better if there were some way to override the default behavior of S{ 5, y:10, s: "hi!" }. But that seems tricky without having named arguments to begin with. Perhaps, though, it would be enough to allow overriding the non-named argument flavors? I.e. you can have an opConstruct that implements S{5,10,"hi"}, but not one for S{5,y:10,s:"hi!"}. Or named arguments get filtered out, so S{5,y:10,s:"hi!"} sets y=10 and s="hi!" then calls S{5}. Too subtle maybe? --bb------ Dreaming, this also opens up the possibility for named function parameters. Consider a function:
Agreed. Struct literals seem like a good way to do named function parameters.struct Args { void* dest; void* src; size_t num; } void memcopy(Args args) { // Copy args.num bytes from args.src to args.dest } ... memcopy(Args{ dst, src, 8 }); // Use ordered params memcopy(Args{ src: a, dest: b, num: a.length }); // Use named params The issue with this is that you have to have a different named struct for every function that takes this style of parameters.
That is a pain.But -- here's the cool trick -- extend typesafe variadic parameters to take structures like they already do for classes... void memcopy(Args args...) // hee hee! memcopy(dst, src, 8); // woah, looks like a normal function.. memcopy(src: a, dest: b, num: a.length); // bam, named params for free! Having colons in the parameter list for a function call is also unambiguous.
Yeh, then maybe you could even extend that to the declaration of the function too with an anonymous struct as the parameter: void memcopy(struct{void *src, void *dest, size_t num}); Seems possible, though not necessarily easy for Mr. Compiler Writer. Main problem is overloading. How do you pick the right version of memcopy if there are several when presented a call like: memcopy(src: a, dest: b, num: a.length); --bb
Jun 24 2008
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:g3rs3g$11ub$1 digitalmars.com...But there will still be the problem that using call syntax for struct construction gets in the way of using structs as functors. There's no way to distinguish between "I'm constructing" and "I'm calling this as a functor". If you're lucky construction parameters and functor parameters don't overlap, but if you're unlucky you must contort your code. Struct construction should not use function call syntax.
Oo. Nasty.They have completely different syntax and features. Static struct initializers are far more powerful: you can name members, initialize them out of order, and skip arbitrary members.
This is the only place where I disagree with you. Static struct initializers are more powerful in the ways you list, but in other ways they are less powerful. With the static opCall you can make the parameters be whatever you want them to be -- they don't necessarily have to correspond to an actual member of the struct. And you can also put arbitrary extra construction logic inside a static opCall.
I'm proposing them precisely because I _don't_ like that currently struct literals and struct constructions overlap syntactically ;) I was thinking that struct literals would not be interceptible, they would be like integer or string literals. And if you want struct construction, use.. the constructor (and whatever syntax that implies). But you do bring up a good point with not being able to distinguish static opCall vs. construction. Still, then you have the reverse problem of what we have now -- S{1, 2, 3}, while not looking like a function call, actually might be! :\Yeh, then maybe you could even extend that to the declaration of the function too with an anonymous struct as the parameter: void memcopy(struct{void *src, void *dest, size_t num});
I seem to remember you being interested in this idea before ;)Seems possible, though not necessarily easy for Mr. Compiler Writer. Main problem is overloading. How do you pick the right version of memcopy if there are several when presented a call like: memcopy(src: a, dest: b, num: a.length);
Tricky, but I'm sure that some (reasonable) constraints could be put on this type of function to make it easier to disambiguate. Failing using structs as named parameters, there's certainly nothing stopping the compiler from allowing named parameters with functions as they are now. They have the names right there :P
Jun 24 2008
Jarrett Billingsley wrote:"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:g3rs3g$11ub$1 digitalmars.com...
They have completely different syntax and features. Static struct initializers are far more powerful: you can name members, initialize them out of order, and skip arbitrary members.
initializers are more powerful in the ways you list, but in other ways they are less powerful. With the static opCall you can make the parameters be whatever you want them to be -- they don't necessarily have to correspond to an actual member of the struct. And you can also put arbitrary extra construction logic inside a static opCall.
I'm proposing them precisely because I _don't_ like that currently struct literals and struct constructions overlap syntactically ;) I was thinking that struct literals would not be interceptible, they would be like integer or string literals. And if you want struct construction, use.. the constructor (and whatever syntax that implies).
Ok. Yeh, I can see some benefit in that too. A POL, Plain Old Literal, to go with your POD, Plain Old Data.But you do bring up a good point with not being able to distinguish static opCall vs. construction. Still, then you have the reverse problem of what we have now -- S{1, 2, 3}, while not looking like a function call, actually might be! :\
I suppose D structs could just do without a static opCall. Actually I think the only real problem is that right now "aninstance()" could be a call to either "void static opCall()" or "void opCall()" when both are defined, and so the compiler gives a conflict message. But if instead of static opCall you had a "constructor" then it wouldn't be considered a candidate for a call like "aninstance()". So hopefully that is basically what Walter has planned for the struct constructors.Yeh, then maybe you could even extend that to the declaration of the function too with an anonymous struct as the parameter: void memcopy(struct{void *src, void *dest, size_t num});
I seem to remember you being interested in this idea before ;)
Oh, yeh. No wonder it seemed to resonate. :-)Seems possible, though not necessarily easy for Mr. Compiler Writer. Main problem is overloading. How do you pick the right version of memcopy if there are several when presented a call like: memcopy(src: a, dest: b, num: a.length);
Tricky, but I'm sure that some (reasonable) constraints could be put on this type of function to make it easier to disambiguate.
One thing that's lacking is that you wouldn't be able to tell which named parameters were set vs which not set.Failing using structs as named parameters, there's certainly nothing stopping the compiler from allowing named parameters with functions as they are now. They have the names right there :P
Except 36 years of experience with C and C++ that makes people expect that the names of formal parameters don't matter. I think the only way to make such a big change palatable at this point is to require some special syntax to use it. --bb
Jun 24 2008
"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:g3s45g$1ork$1 digitalmars.com...Tricky, but I'm sure that some (reasonable) constraints could be put on this type of function to make it easier to disambiguate.
One thing that's lacking is that you wouldn't be able to tell which named parameters were set vs which not set.
Ooh, yeah.Failing using structs as named parameters, there's certainly nothing stopping the compiler from allowing named parameters with functions as they are now. They have the names right there :P
Except 36 years of experience with C and C++ that makes people expect that the names of formal parameters don't matter. I think the only way to make such a big change palatable at this point is to require some special syntax to use it.
Funny, I don't feel the same way, probably because I've used D more than I have C or C++ ;)
Jun 24 2008
Jarrett Billingsley wrote:"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:g3s45g$1ork$1 digitalmars.com...Tricky, but I'm sure that some (reasonable) constraints could be put on this type of function to make it easier to disambiguate.
parameters were set vs which not set.
Ooh, yeah.Failing using structs as named parameters, there's certainly nothing stopping the compiler from allowing named parameters with functions as they are now. They have the names right there :P
the names of formal parameters don't matter. I think the only way to make such a big change palatable at this point is to require some special syntax to use it.
Funny, I don't feel the same way, probably because I've used D more than I have C or C++ ;)
Ok, but it would have impact on a backlog of 10 years' worth of D code too. I agree it would be cool to have, but I also agree with Walter that switching D's default scheme to named parameters at this point would incur too much cost for too little benefit. But when whoever sits down to start writing D++ or E, I hope he gives named parameters some serious thought. That and putting the type after the variable instead of before. --bb
Jun 24 2008
Bill Baxter wrote:Jarrett Billingsley wrote:"Bill Baxter" <dnewsgroup billbaxter.com> wrote in message news:g3s45g$1ork$1 digitalmars.com...Tricky, but I'm sure that some (reasonable) constraints could be put on this type of function to make it easier to disambiguate.
named parameters were set vs which not set.
Ooh, yeah.Failing using structs as named parameters, there's certainly nothing stopping the compiler from allowing named parameters with functions as they are now. They have the names right there :P
that the names of formal parameters don't matter. I think the only way to make such a big change palatable at this point is to require some special syntax to use it.
Funny, I don't feel the same way, probably because I've used D more than I have C or C++ ;)
Ok, but it would have impact on a backlog of 10 years' worth of D code too. I agree it would be cool to have, but I also agree with Walter that switching D's default scheme to named parameters at this point would incur too much cost for too little benefit. But when whoever sits down to start writing D++ or E, I hope he gives named parameters some serious thought. That and putting the type after the variable instead of before. --bb
For what it's worth, if you really want you can fake named parameters with templates and typedefs. typedef int _Foo; _Foo Foo(int i) { return cast(_Foo) i; } void test(T...)(T t) { // test for presence of a _Foo in t } void main() { test(Foo=5); }
Jun 25 2008









Robert Fraser <fraserofthenight gmail.com> 