www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Proposal: real struct literals

reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
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
next sibling parent Robert Fraser <fraserofthenight gmail.com> writes:
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
prev sibling next sibling parent reply Mike <vertex gmx.at> writes:
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
parent "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Jarrett Billingsley" wrote
 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!" };
Agree 100% -Steve
Jun 24 2008
prev sibling parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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.
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).
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
parent reply "Jarrett Billingsley" <kb3ctd2 yahoo.com> writes:
"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
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
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.
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++ ;)
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
parent downs <default_357-line yahoo.de> writes:
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.
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++ ;)
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