Jump to page: 1 2
Thread overview
Proposal: real struct literals
Jun 24, 2008
Robert Fraser
Jun 24, 2008
Mike
Jun 24, 2008
Bill Baxter
Jun 25, 2008
Bill Baxter
Jun 25, 2008
Bill Baxter
Jun 25, 2008
downs
June 24, 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.


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

June 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/
June 24, 2008
"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


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


June 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
June 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


June 25, 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.
>> 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
June 25, 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++ ;)


June 25, 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.
>> 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
« First   ‹ Prev
1 2