www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Anonymous structs

reply Jacob Carlborg <doob me.com> writes:
I've been thinking for this feature for a while. An anonymous struct is 
basically what it sound like, a struct without a name. The inserting 
part is its members. If two anonymous structs have the same members (not 
necessarily in the same order) they are considered to be of the same 
type. This shows how anonymous structs looks like:

{ int x, int y } point = { y: 4, x: 5 };
auto point2 = { x: 1, y: 2 };
point = point2;

In this example "point" and "point2" are of the same type since they 
have the same members. This is basically lowered to something similar to:

struct __AnonymousStruct_int_x_int_y
{
     int x;
     int y;
}

__AnonymousStruct_int_x_int_y point;
point.y = 4;
point.x = 5;

__AnonymousStruct_int_x_int_y point2;
point2.x = 1;
point2.y = 2;
point = point2;

The compiler will implicitly generate a new struct type with a name that 
will always be the same if it has the same members. These structs will 
then behave just like any other structs, accessing members and so on.

assert(point2.x == 1);
assert(point.y == 2);

The advantage of anonymous structs is that they can be declared in 
place, in function declartions for example:

void foo ({ int x, int y } point)
{
}

foo({ y: 5, x: 3 });

When calling a function and passing in an anonymous structs it's 
possible to drop the braces to get a more pleasing and less verbose syntax:

foo(y: 5, x: 3);

With this syntax sugar we can basically get named parameters. With the 
braces it also looks like JSON.

Anonymous structs can be implicitly converted to and from regular named 
struts:

* Named struct to anonymous struct:

struct Bar
{
     int x;
     int y;
}

foo(Bar(1, 2));

* Anonymous struct to named struct:

void bar (Bar b) {}

bar(x: 3, y: 4);

It would also be nice to have opDispatch soak up everything that doesn't 
match, if it's declared in a named struct:

struct Options
{
     int x;
     int y;
     private Variant[string] members;

     void opDispatch (string name, T) (T t)
     {
         members[name] = Variant(t);
     }
}

void fooBar (Options options) {}

fooBar(x: 5, a: "asd", y: 1.2, b: 4);

"x" and "y" are matched with the regular members, "a" and "b" are 
matched to opDispatch which takes care of them.

Perhaps this initializer syntax can be used for classes as well:

class FooBar
{
     int x;
     int y;
}

FooBar fb = { x: 3, y: 5 };

The above would be lowered to:

FooBar fb = new FooBar();
fb.x = 3;
fb.y = 5;

Thoughts?

-- 
/Jacob Carlborg
Feb 11 2013
next sibling parent reply Nick Sabalausky <SeeWebsiteToContactMe semitwist.com> writes:
Isn't that all basically syntax sugar for tuples?
Feb 11 2013
next sibling parent Jacob Carlborg <doob me.com> writes:
On 2013-02-11 22:54, Nick Sabalausky wrote:
 Isn't that all basically syntax sugar for tuples?
I didn't really thing of tuples, but it might be considered that. I assume you are referring to std.typecons.Tuple? Except the syntax, there are some limitations with Tuple. It cannot be implicitly converted to other types based on its members. I don't know if that's possible to fix. It can also contain just types, or just values. BTW, isn't tuples and structs basically the same thing in the end. -- /Jacob Carlborg
Feb 11 2013
prev sibling next sibling parent Russel Winder <russel winder.org.uk> writes:
On Mon, 2013-02-11 at 16:54 -0500, Nick Sabalausky wrote:
 Isn't that all basically syntax sugar for tuples?
At EuroPython 2010 Guido van Rossum reported that tuple literals were always intended to be thought of as struct literals. --=20 Russel. =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel.winder ekiga.n= et 41 Buckmaster Road m: +44 7770 465 077 xmpp: russel winder.org.uk London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Feb 12 2013
prev sibling parent reply Martin Nowak <code dawg.eu> writes:
On 02/11/2013 10:54 PM, Nick Sabalausky wrote:
 Isn't that all basically syntax sugar for tuples?
The tuple problem again was my first thought too. http://d.puremagic.com/issues/show_bug.cgi?id=6365
Feb 13 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-02-13 18:42, Martin Nowak wrote:

 The tuple problem again was my first thought too.
 http://d.puremagic.com/issues/show_bug.cgi?id=6365
That looks quite similar. I'll have to read through the whole issue. -- /Jacob Carlborg
Feb 13 2013
prev sibling next sibling parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Monday, 11 February 2013 at 21:30:52 UTC, Jacob Carlborg wrote:
 I've been thinking for this feature for a while. An anonymous 
 struct is basically what it sound like, a struct without a 
 name. The inserting part is its members. If two anonymous 
 structs have the same members (not necessarily in the same 
 order) they are considered to be of the same type. This shows 
 how anonymous structs looks like:

 { int x, int y } point = { y: 4, x: 5 };
 auto point2 = { x: 1, y: 2 };
 point = point2;

 In this example "point" and "point2" are of the same type since 
 they have the same members. This is basically lowered to 
 something similar to:
What if there's another anonymous struct that has a little more? { int x, int y } point = { y: 4, x: 5 }; { int x, int y, int color } color_point = { y: 4, x: 5, color: 0x000000 }; //which anonymous struct does it go with? //Or can auto only work with named/returned structs? auto point2 = { x: 1, y: 2 }; point = point2; //error, point2 type void _error We can instantiate structs with fewer arguments than elements they hold. It would either have to go the safe route, or try it's best to either be 'safe' or 'whatever seems to work' until you change something and break it utterly with no description of 'what' it is. Anonymous structs would be more useful as single instances like unions can be in C. (I think it's disallowed in D, or I had trouble with them) Nick Sabalausky wrote:
Isn't that all basically syntax sugar for tuples?
Maybe. I would think an explicit tuple would be better.
Feb 11 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-02-11 23:20, Era Scarecrow wrote:

   What if there's another anonymous struct that has a little more?

    { int x, int y } point = { y: 4, x: 5 };
    { int x, int y, int color } color_point
            = { y: 4, x: 5, color: 0x000000 };
    //which anonymous struct does it go with?
    //Or can auto only work with named/returned structs?
    auto point2 = { x: 1, y: 2 };

    point = point2; //error, point2 type void _error
"point2" is completely independent of the other declarations. But you can think of it having the same type as "point". It can also be implicitly converted to "color_point". It's not the actual type that's interesting, it's the members. The compiler checks the members to see if two values are of the same types.
   We can instantiate structs with fewer arguments than elements they
 hold. It would either have to go the safe route, or try it's best to
 either be 'safe' or 'whatever seems to work' until you change something
 and break it utterly with no description of 'what' it is.
It's not the actual type that's interesting, as long as the members match they're considered to be the same type. -- /Jacob Carlborg
Feb 11 2013
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Tuesday, 12 February 2013 at 07:46:31 UTC, Jacob Carlborg 
wrote:
 On 2013-02-11 23:20, Era Scarecrow wrote:

  What if there's another anonymous struct that has a little 
 more?

   { int x, int y } point = { y: 4, x: 5 };
   { int x, int y, int color } color_point
           = { y: 4, x: 5, color: 0x000000 };
   //which anonymous struct does it go with?
   //Or can auto only work with named/returned structs?
   auto point2 = { x: 1, y: 2 };

   point = point2; //error, point2 type void _error
"point2" is completely independent of the other declarations. But you can think of it having the same type as "point". It can also be implicitly converted to "color_point". It's not the actual type that's interesting, it's the members. The compiler checks the members to see if two values are of the same types.
Maybe if it was an interpreted language or scripting, but not statically typed. Just because they are anonymous doesn't mean they suddenly have new 'alias this' members written to convert from one type to the other. auto point2 = {color: 0x00ff00}; //second definition probably point2 = point; //converts how? May be safe.. point = point2; //loss of data...
 We can instantiate structs with fewer arguments than elements 
 they hold. It would either have to go the safe route, or try 
 it's best to either be 'safe' or 'whatever seems to work' 
 until you change something and break it utterly with no 
 description of 'what' it is.
It's not the actual type that's interesting, as long as the members match they're considered to be the same type.
Really? Wow... Sounds like an interface... So this means it would auto deduce it's type based on matching... /*each struct could be a lot more complex, but so long as 'x' is present and assignable, then the lower ones allow it to be potentially correct */ struct S{int x; enum y = 0;} struct T{int x; enum y = "str";} struct U{int x; enum y = 0.45;} struct V{int x; enum y = [1, 2];} {int x; enum junk = false;} something = {}; {int x; enum junk = ["some","thing"];} something2 = {}; assert(something.y == false); //probably assert(something2.y == ["some","thing"]); //probably auto x = {}; //legally could be S, T, U or V, or anonymous(s) auto x3 = {45}; //legally could be S, T, U or V, or anonymous(s) auto x2 = {x: 45}; //legally could be S, T, U or V, or anonymous(s) //may or may not work, 1 in 6 as correct. //if these must be true, then they are all S (or annonymous1) assert(x.y == 0); assert(x2.y == 0); assert(x3.y == 0); No, deducing types like this won't work for static typing, be they anonymous or not. The only way they'd work is if they only existed for the one instance, being point or point_color, but auto wouldn't allow you to auto determine it; But still that doesn't seem like a good syntax to have and I'd rather take the extra line to define the type to ensure it's uniqe, Or tuples if it's only data..
Feb 12 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-02-12 13:08, Era Scarecrow wrote:
 On Tuesday, 12 February 2013 at 07:46:31 UTC, Jacob Carlborg wrote:
 On 2013-02-11 23:20, Era Scarecrow wrote:

  What if there's another anonymous struct that has a little more?

   { int x, int y } point = { y: 4, x: 5 };
   { int x, int y, int color } color_point
           = { y: 4, x: 5, color: 0x000000 };
   //which anonymous struct does it go with?
   //Or can auto only work with named/returned structs?
   auto point2 = { x: 1, y: 2 };

   point = point2; //error, point2 type void _error
"point2" is completely independent of the other declarations. But you can think of it having the same type as "point". It can also be implicitly converted to "color_point". It's not the actual type that's interesting, it's the members. The compiler checks the members to see if two values are of the same types.
Maybe if it was an interpreted language or scripting, but not statically typed. Just because they are anonymous doesn't mean they suddenly have new 'alias this' members written to convert from one type to the other.
You don't seem to get how I want it to work.
    auto point2 = {color: 0x00ff00}; //second definition probably
This has nothing to do with some other struct. It's lowered to something like: struct __AnonymousStruct_int_color { int color; } point2.color = 0x00ff00; It has nothing to do with "color_point".
    point2 = point; //converts how? May be safe..
Since "point" is declared as: { int x, int y } point; And "point2" is declared as: { int color } point2; It's a type error, the members doesn't match.
    point = point2; //loss of data...
Same as above.
   Really? Wow... Sounds like an interface...
Yes, in a way.
   So this means it would auto deduce it's type based on matching...
When "auto" is used it doesn't try to match on anything else.
    /*each struct could be a lot more complex, but so long as 'x' is
 present and assignable, then the lower ones allow it to be potentially
 correct */
    struct S{int x; enum y = 0;}
    struct T{int x; enum y = "str";}
    struct U{int x; enum y = 0.45;}
    struct V{int x; enum y = [1, 2];}
    {int x; enum junk = false;} something = {};
    {int x; enum junk = ["some","thing"];} something2 = {};

    assert(something.y == false); //probably
Type error. "something" doesn't have the member "y".
    assert(something2.y == ["some","thing"]); //probably
Same as above.
    auto x  = {};      //legally could be S, T, U or V, or anonymous(s)
No, it has nothing to do with any other declared struct. Don't even now if this would be legal, since it's kind of pointless do have an anonymous struct without members.
    auto x3 = {45};    //legally could be S, T, U or V, or anonymous(s)
No, same as above.
    auto x2 = {x: 45}; //legally could be S, T, U or V, or anonymous(s)
Legal, is and can only be anonymous. It has nothing to do with S, T, U or V. It's anonymous, period.
    //may or may not work, 1 in 6 as correct.
    //if these must be true, then they are all S (or annonymous1)
    assert(x.y == 0);
    assert(x2.y == 0);
    assert(x3.y == 0);
Compile error for all, since none have the member "y".
   No, deducing types like this won't work for static typing, be they
 anonymous or not. The only way they'd work is if they only existed for
 the one instance, being point or point_color, but auto wouldn't allow
 you to auto determine it; But still that doesn't seem like a good syntax
 to have and I'd rather take the extra line to define the type to ensure
 it's uniqe, Or tuples if it's only data..
You obviously don't understand how I want it to work. -- /Jacob Carlborg
Feb 12 2013
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Tuesday, 12 February 2013 at 14:10:41 UTC, Jacob Carlborg 
wrote:
 On 2013-02-12 13:08, Era Scarecrow wrote:
 No, deducing types like this won't work for static typing, be 
 they anonymous or not. The only way they'd work is if they 
 only existed for the one instance, being point or point_color, 
 but auto wouldn't allow you to auto determine it; But still 
 that doesn't seem like a good syntax to have and I'd rather 
 take the extra line to define the type to ensure it's uniqe, 
 Or tuples if it's only data..
You obviously don't understand how I want it to work.
Seems I did misread what you had, however having it creating dozens of misc/anonymous types doesn't seem like a wise idea. The entire block as it was defined is more like a scope/code block rather than a struct declaration; Then is it a delegate instead? (without return type or input type possibly) int x = 100; int y = { int z; int isPrime(int n); z = x * 100; // z = isPrime(100); //alternate to avoid 'nested' }; //was code block run? (delegate or anonymous function) assert(y.z == 10000); writeln(y.z); //allowed? writeln(y.isprime(x)); //allowed? y(); //allowed? //if y is code block can be run... //last line is return line? Or not? assert(y() == x*100); // assert(y() == 0); //isprime version Now if it relies on x or not, is it now nested or not? Keep in mind the following is completely legal in C/C++/D. I've used this before to help separate and fold code. { //inner scope { int tmp = 256; //some code involving tmp } }
Feb 12 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-02-12 21:30, Era Scarecrow wrote:

   Seems I did misread what you had, however having it creating dozens of
 misc/anonymous types doesn't seem like a wise idea. The entire block as
 it was defined is more like a scope/code block rather than a struct
 declaration; Then is it a delegate instead? (without return type or
 input type possibly)
I don't know what you're talking about. Where did "delegate" come from? -- /Jacob Carlborg
Feb 12 2013
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Wednesday, 13 February 2013 at 07:28:14 UTC, Jacob Carlborg 
wrote:
 On 2013-02-12 21:30, Era Scarecrow wrote:

 Seems I did misread what you had, however having it creating 
 dozens of misc/anonymous types doesn't seem like a wise idea. 
 The entire block as it was defined is more like a scope/code 
 block rather than a struct declaration; Then is it a delegate 
 instead? (without return type or input type possibly)
I don't know what you're talking about. Where did "delegate" come from?
Then let's step back. You can make a scope block without having 'if' or any other statment that separates it. unittest { int x; { x++;//code block is valid } Now if you attach that to a variable it's effectively a delegate, function, or predicate; depending on syntax of how it's called. auto y = delegate void(){ x++; }; auto y = (){ x++; }; //shortened to auto y = { x++; }; //if no calling variables gets shortened to..?? Now if there's only type declarations and no instructions, it can be an anonymous struct (probably), but what if it has code? Is it a code block? The code gets defaulted to a function inside it? Illegal to do period? (at which point it breaks regular compatibility most likely). auto z = {int x,y,color;}; //types only, could be struct... auto z = { //which is it? int x,y,color; x++; y++; color = 0xffffff; }; This can either be 1) POD struct, instructions are illegal 2) instructions called right away, still a POD struct otherwise auto z = { int x,y,color; }; z.x++; z.y++; z.color = 0xffffff; 3) delegate/function/predicate z(); //call is legal, or passable to template function 4) structs legal and instructions are effectively postblit (or opCall or something) struct anonymous_int_x_y_color { int x,y,color; this(this) { x++; y++; color = 0xffffff; } } anonymous_int_x_y_color z; } 5) none of it is legal, leaves you having to specify what it is which is probably safer than assumption or ambiguity. It can be easy to forget a simple set of parenthesis (or semicolon, or equal sign) sometimes when you're programming, having it make assumptions of code 'this is a struct' vs 'this is a delegate' could be quite the annoyance, perhaps with very confusing error messages. IMO the way you're suggest having anonymous structs seems unneeded.
Feb 13 2013
parent reply Jacob Carlborg <doob me.com> writes:
On 2013-02-13 09:45, Era Scarecrow wrote:

   Then let's step back. You can make a scope block without having 'if'
 or any other statment that separates it.

    unittest {
      int x;

      {
        x++;//code block is valid
      }

   Now if you attach that to a variable it's effectively a delegate,
 function, or predicate; depending on syntax of how it's called.

      auto y = delegate void(){ x++; };
      auto y = (){ x++; }; //shortened to
      auto y = { x++; };   //if no calling variables gets shortened to..??

   Now if there's only type declarations and no instructions, it can be
 an anonymous struct (probably), but what if it has code? Is it a code
 block? The code gets defaulted to a function inside it? Illegal to do
 period? (at which point it breaks regular compatibility most likely).
My suggestion is for anonymous structs, nothing else. It can only contain declarations of fields. I would thought that was pretty clear, especially since the subject says "Anonymous structs" and not something like "Anonymous delegates". I also showed how the syntax is lowered into a regular named struct, which would make things even more clear. But apparently not. Is my English so bad or is it just the idea that is so bad? -- /Jacob Carlborg
Feb 13 2013
parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Wednesday, 13 February 2013 at 10:35:08 UTC, Jacob Carlborg 
wrote:
 My suggestion is for anonymous structs, nothing else. It can 
 only contain declarations of fields. I would thought that was 
 pretty clear, especially since the subject says "Anonymous 
 structs" and not something like "Anonymous delegates".
I'm aware of that.
 I also showed how the syntax is lowered into a regular named 
 struct, which would make things even more clear. But apparently 
 not.

 Is my English so bad or is it just the idea that is so bad?
More like how to make it syntactically proper/unambiguous & compatible so the compiler could identify and use it properly; Sudden new use(s) of code blocks without somehow clarifying it's intended use (before hand) could be a problem, or worse yet, prevent something better later if it's introduced & used (and you get the same C++ issues where you can't fix/change something without breaking anything relying on a defined feature). Remember, just cause it seems simple (to us) doesn't mean it's simple. Maybe I'm thinking too far ahead... How much extra complexity would be be to add the feature? If we're using say Lex & Yacc for example a simple feature would be only a couple lines; If you need to make whole new branch(es) then it may not be a good idea. If it requires complex rules, then it may not be reliable as we won't remember them all while we're programming.
Feb 13 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-02-13 14:02, Era Scarecrow wrote:

   More like how to make it syntactically proper/unambiguous & compatible
 so the compiler could identify and use it properly; Sudden new use(s) of
 code blocks without somehow clarifying it's intended use (before hand)
 could be a problem, or worse yet, prevent something better later if it's
 introduced & used (and you get the same C++ issues where you can't
 fix/change something without breaking anything relying on a defined
 feature). Remember, just cause it seems simple (to us) doesn't mean it's
 simple.

   Maybe I'm thinking too far ahead... How much extra complexity would be
 be to add the feature? If we're using say Lex & Yacc for example a
 simple feature would be only a couple lines; If you need to make whole
 new branch(es) then it may not be a good idea. If it requires complex
 rules, then it may not be reliable as we won't remember them all while
 we're programming.
I think this all sounds like a big misunderstanding. I though you wanted to turn my proposal to some kind of delegate. Sometimes it's better to very clear and spell out exactly what one mean, something like: "This syntax will/could conflict with delegates". -- /Jacob Carlborg
Feb 13 2013
prev sibling next sibling parent reply "MattCoder" <mattcoder hotmail.com> writes:
On Monday, 11 February 2013 at 21:30:52 UTC, Jacob Carlborg wrote:
 The advantage of anonymous structs is that they can be declared 
 in place, in function declartions for example:

 void foo ({ int x, int y } point)
 {
 }

 foo({ y: 5, x: 3 });
At the first look it seems interesting, but imagine that you need to change one type or add more members to that struct, have you imagined the mess to change all those declarations? Because that case I prefer the old way: void foo(MyPointStruct point) { } Any changing in "MyPointStruct" will be consumed by all the code.
Feb 11 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-02-11 23:58, MattCoder wrote:
 On Monday, 11 February 2013 at 21:30:52 UTC, Jacob Carlborg wrote:
 The advantage of anonymous structs is that they can be declared in
 place, in function declartions for example:

 void foo ({ int x, int y } point)
 {
 }

 foo({ y: 5, x: 3 });
At the first look it seems interesting, but imagine that you need to change one type or add more members to that struct, have you imagined the mess to change all those declarations?
If you add a new member to the anonymous struct declared in "foo" any existing call will still match. That's just like the current initializer syntax works: struct Bar { int a; int b; } Bar bar = { b: }; // "a" is default initialized If you remove a member from the anonymous struct declared in "foo" you will get a compile error since there will be a member that doesn't match.
 Because that case I prefer the old way:

 void foo(MyPointStruct point)
 {
 }

 Any changing in "MyPointStruct" will be consumed by all the code.
I'm not sure I see the difference compared with the anonymous struct. -- /Jacob Carlborg
Feb 11 2013
prev sibling next sibling parent reply "Brian Schott" <briancschott gmail.com> writes:
On Monday, 11 February 2013 at 21:30:52 UTC, Jacob Carlborg wrote:
 { int x, int y } point = { y: 4, x: 5 };
That looks like it would confuse the parser because it looks almost exactly like a BlockStatement until you get several tokens in.
Feb 11 2013
parent Jacob Carlborg <doob me.com> writes:
On 2013-02-12 00:17, Brian Schott wrote:

 That looks like it would confuse the parser because it looks almost
 exactly like a BlockStatement until you get several tokens in.
That might be the case, I have no idea. Is there another syntax that would work better that could fit in a function declaration as well? -- /Jacob Carlborg
Feb 11 2013
prev sibling parent "Michael" <pr m1xa.com> writes:
In some DSL a struct can be defined as something like:

auto myStruct = new Struct("a:int; b:string; c:boolean");

So this looks like a simple dictionary or AA like Variant[string] 
myStruct.

I don't see big difference in use. So D Way approach is Tuple 
-simple and good enough.
Feb 13 2013