www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - String mixin syntax sugar

reply Mantis <mail.mantis.88 gmail.com> writes:
Hello,

since people discussed a lot about user-defined attributes recently, 
I've been thinking about a way to implement it with a string mixins. The 
problem with them is their syntax - it's far from what we want to use in 
everyday job. I understand, they should be easily distinguished at use 
site, but perhaps this may be accomplished in other ways as well. My 
idea is to translate this kind of statements:



into this:

mixin( identifier( q{ statement } ) );

where an identifier is a, possibly templated, function that accepts one 
string argument and returns a string. Here are some possible use cases:

#serialize         int a; // marked to be serializable
#serialize!not int b; // -.- non-serializable

#readonly float c; // generate trivial private setter and public getter

#handles!Events.Foo void handler(); // event handler

#attribute!"Foo" void foo(); // function with additional compile-time info

Most of these examples require some D parser, but, since it is planned 
to add parser-generation into Phobos, this shouldn't be a problem.
What do you think, does it have some value for the language, and, if 
yes, is it possible to implement?
Mar 20 2012
next sibling parent reply Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On 20.03.2012 22:25, Mantis wrote:
 Hello,

 since people discussed a lot about user-defined attributes recently,
 I've been thinking about a way to implement it with a string mixins. The
 problem with them is their syntax - it's far from what we want to use in
 everyday job.
Use template mixins? At least in struct/class area that catches the most attention it should work perfectly. I understand, they should be easily distinguished at use
 site, but perhaps this may be accomplished in other ways as well. My
 idea is to translate this kind of statements:



 into this:

 mixin( identifier( q{ statement } ) );

 where an identifier is a, possibly templated, function that accepts one
 string argument and returns a string. Here are some possible use cases:

 #serialize int a; // marked to be serializable
 #serialize!not int b; // -.- non-serializable

 #readonly float c; // generate trivial private setter and public getter
Why not just leave it alone, and (if need comes) turn it into property on demand.
 #handles!Events.Foo void handler(); // event handler

 #attribute!"Foo" void foo(); // function with additional compile-time info
It's just voodoo - no guaranties that #attribute can do that given the above rewrite.
 Most of these examples require some D parser, but, since it is planned
 to add parser-generation into Phobos, this shouldn't be a problem.
 What do you think, does it have some value for the language, and, if
 yes, is it possible to implement?
Looks like throwing nukes to pin down a pesky cockroach. The sugar is more or less nice, but encourages ineffective compile-time wise idiom. -- Dmitry Olshansky
Mar 20 2012
parent d coder <dlang.coder gmail.com> writes:
 Use template mixins? At least in struct/class area that catches the most
 attention it should work perfectly.
I believe template mixins are equally ugly. Don't you have to explicitly specify that you are invoking a mixin? Or am I missing something here. Regards - Puneet
Mar 20 2012
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2012-03-20 19:25, Mantis wrote:
 Hello,

 since people discussed a lot about user-defined attributes recently,
 I've been thinking about a way to implement it with a string mixins. The
 problem with them is their syntax - it's far from what we want to use in
 everyday job. I understand, they should be easily distinguished at use
 site, but perhaps this may be accomplished in other ways as well. My
 idea is to translate this kind of statements:



 into this:

 mixin( identifier( q{ statement } ) );
I don't like it. I want real user defined attributes. -- /Jacob Carlborg
Mar 20 2012
parent "Tove" <tove fransson.se> writes:
On Tuesday, 20 March 2012 at 21:28:25 UTC, Jacob Carlborg wrote:
 On 2012-03-20 19:25, Mantis wrote:
 Hello,

 since people discussed a lot about user-defined attributes 
 recently,
 I've been thinking about a way to implement it with a string 
 mixins. The
 problem with them is their syntax - it's far from what we want 
 to use in
 everyday job. I understand, they should be easily 
 distinguished at use
 site, but perhaps this may be accomplished in other ways as 
 well. My
 idea is to translate this kind of statements:



 into this:

 mixin( identifier( q{ statement } ) );
I don't like it. I want real user defined attributes.
I think the idea has merit, string mixins together with CTFE parsing is the holy grail... because of the current syntax it's not really feasible to use on a per-member basis... but it is possible to use on a struct/class basis... mixin(attr(q{struct Foo { NonSerialized int x; NonSerialized int y; int z; }})); Please disregard my broken parser(it's just a proof of concept) However, consider what we could do with the latest CTFE parser advances, coupled with a tighter compiler/library callback interface. ==================================================================== import std.stdio; import std.array; import std.string; import std.algorithm; string attr(string complex_decl) { string org_struct; string ser_struct; auto lines = splitLines(complex_decl); { auto decl = split(stripLeft(lines[0])); if(decl[0]=="struct") { org_struct = decl[0] ~ " " ~ decl[1]; ser_struct = decl[0] ~ " " ~ decl[1] ~ "_Serializable"; } else return complex_decl; } foreach(line; lines[1..$]) { auto attr = findSplitAfter(stripLeft(line), " NonSerialized "); if(attr[0]==" NonSerialized ") org_struct ~= attr[1]; else { org_struct ~= attr[1]; ser_struct ~= attr[1]; } } return ser_struct ~ "\n" ~ org_struct; } mixin(attr(q{struct Foo { NonSerialized int x; NonSerialized int y; int z; }})); void main() { auto m = [ __traits(allMembers, Foo) ]; writeln("Normal members of Foo:", m); auto n = [ __traits(allMembers, Foo_Serializable) ]; writeln("Serializable members of Foo:", n); }
Mar 20 2012
prev sibling next sibling parent bcs <bcs example.com> writes:
On 03/20/2012 11:25 AM, Mantis wrote:
 Hello,

 since people discussed a lot about user-defined attributes recently,
 I've been thinking about a way to implement it with a string mixins. The
 problem with them is their syntax - it's far from what we want to use in
 everyday job. I understand, they should be easily distinguished at use
 site, but perhaps this may be accomplished in other ways as well. My
 idea is to translate this kind of statements:



 into this:

 mixin( identifier( q{ statement } ) );
No no no no, oh the humanity no! String mixins should be a method of LAST resort. Please don't add sugar to make them look nicer. If anything add vinegar and make them look as ugly as they really are.
 where an identifier is a, possibly templated, function that accepts one
 string argument and returns a string. Here are some possible use cases:

 #serialize int a; // marked to be serializable
 #serialize!not int b; // -.- non-serializable

 #readonly float c; // generate trivial private setter and public getter

 #handles!Events.Foo void handler(); // event handler

 #attribute!"Foo" void foo(); // function with additional compile-time info

 Most of these examples require some D parser, but, since it is planned
 to add parser-generation into Phobos, this shouldn't be a problem.
 What do you think, does it have some value for the language, and, if
 yes, is it possible to implement?
Mar 20 2012
prev sibling parent reply kennytm <kennytm gmail.com> writes:
Mantis <mail.mantis.88 gmail.com> wrote:
 Hello,
 
 since people discussed a lot about user-defined attributes recently, I've
 been thinking about a way to implement it with a string mixins. The
 problem with them is their syntax - it's far from what we want to use in
 everyday job. I understand, they should be easily distinguished at use
 site, but perhaps this may be accomplished in other ways as well. My idea
 is to translate this kind of statements:
 

 
You mean 'declaration'.
 into this:
 
 mixin( identifier( q{ statement } ) );
 
 where an identifier is a, possibly templated, function that accepts one
 string argument and returns a string. Here are some possible use cases:
 
 #serialize         int a; // marked to be serializable
 #serialize!not int b; // -.- non-serializable
 
 #readonly float c; // generate trivial private setter and public getter
 
 #handles!Events.Foo void handler(); // event handler
 
 #attribute!"Foo" void foo(); // function with additional compile-time info
 
 Most of these examples require some D parser, but, since it is planned to
 add parser-generation into Phobos, this shouldn't be a problem.
 What do you think, does it have some value for the language, and, if yes,
 is it possible to implement?
The syntax may conflict with '#line'. What to do where there are multiple attributes? E.g. #license!"BSD" safe #memoize pure nothrow auto invertMatrix(T)(T[] elements) if (isArithmetic!T) { ... } What to do with .di files? // in .di #handler void onLoad(); // in .d #handler void onLoad() { .... } Besides, what's wrong with using ' '? serialize int a;
Mar 21 2012
parent reply Mantis <mail.mantis.88 gmail.com> writes:
21.03.2012 13:35, kennytm пишет:
 Mantis<mail.mantis.88 gmail.com>  wrote:
 [...]

You mean 'declaration'.
Not necessarily, this could be used anywhere a mixin can be.
 [...]
The syntax may conflict with '#line'.
I didn't know. The choice for the symbol is not that important however.
 What to do where there are multiple attributes? E.g.

 #license!"BSD"  safe #memoize pure nothrow auto invertMatrix(T)(T[]
 elements) if (isArithmetic!T) { ... }
Evaluation order stays the same as in this example, the improvement is purely syntactical: // import std.stdio, std.array; string m1( string s ) { return replace( s, "foo", "bar" ); } string m2( string s ) { return replace( s, "bar", "baz" ); } mixin( m1( q{ mixin( m2( q{ void foo( int i ) { writeln( i ); }}));})); void main() { baz( 42 ); } //
 What to do with .di files?

 // in .di
 #handler void onLoad();
 // in .d
 #handler void onLoad() { .... }
Since the function receives a string, it can deal with declarations differently from definitions, it's only a matter of parser.
 Besides, what's wrong with using ' '?

  serialize int a;
Builtin annotations' names are not reserved keywords, so this would be possible and ambiguous: string safe( string s ); safe void foo(); // what safe stands for?
Mar 21 2012
next sibling parent dennis luehring <dl.soluz gmx.net> writes:
and what about attribute parameters like  serialize(version=2)

Am 21.03.2012 13:35, schrieb Mantis:
 21.03.2012 13:35, kennytm пишет:
  Mantis<mail.mantis.88 gmail.com>   wrote:
  [...]

You mean 'declaration'.
Not necessarily, this could be used anywhere a mixin can be.
  [...]
The syntax may conflict with '#line'.
I didn't know. The choice for the symbol is not that important however.
  What to do where there are multiple attributes? E.g.

  #license!"BSD"  safe #memoize pure nothrow auto invertMatrix(T)(T[]
  elements) if (isArithmetic!T) { ... }
Evaluation order stays the same as in this example, the improvement is purely syntactical: // import std.stdio, std.array; string m1( string s ) { return replace( s, "foo", "bar" ); } string m2( string s ) { return replace( s, "bar", "baz" ); } mixin( m1( q{ mixin( m2( q{ void foo( int i ) { writeln( i ); }}));})); void main() { baz( 42 ); } //
  What to do with .di files?

  // in .di
  #handler void onLoad();
  // in .d
  #handler void onLoad() { .... }
Since the function receives a string, it can deal with declarations differently from definitions, it's only a matter of parser.
  Besides, what's wrong with using ' '?

   serialize int a;
Builtin annotations' names are not reserved keywords, so this would be possible and ambiguous: string safe( string s ); safe void foo(); // what safe stands for?
Mar 21 2012
prev sibling next sibling parent "RivenTheMage" <riven-mage id.ru> writes:
On Wednesday, 21 March 2012 at 12:37:50 UTC, Mantis wrote:
 Builtin annotations' names are not reserved keywords, so this 
 would be possible and ambiguous:

 string safe( string s );
  safe void foo(); // what  safe stands for?
What about " " for built-in annotations and " !" for user-defined? !license("BSD") safe !memoize pure nothrow auto invertMatrix(T)(T[] elements) if (isArithmetic!T) { ... }
Mar 21 2012
prev sibling parent kennytm <kennytm gmail.com> writes:
Mantis <mail.mantis.88 gmail.com> wrote:
 21.03.2012 13:35, kennytm пишет:
 Mantis<mail.mantis.88 gmail.com>  wrote:
 [...]

 
You mean 'declaration'.
Not necessarily, this could be used anywhere a mixin can be.
 [...]
The syntax may conflict with '#line'.
I didn't know. The choice for the symbol is not that important however.
 What to do where there are multiple attributes? E.g.
 
 #license!"BSD"  safe #memoize pure nothrow auto invertMatrix(T)(T[]
 elements) if (isArithmetic!T) { ... }
Evaluation order stays the same as in this example, the improvement is purely syntactical: // import std.stdio, std.array; string m1( string s ) { return replace( s, "foo", "bar" ); } string m2( string s ) { return replace( s, "bar", "baz" ); } mixin( m1( q{ mixin( m2( q{ void foo( int i ) { writeln( i ); }}));})); void main() { baz( 42 ); } //
 What to do with .di files?
 
 // in .di
 #handler void onLoad();
 // in .d
 #handler void onLoad() { .... }
Since the function receives a string, it can deal with declarations differently from definitions, it's only a matter of parser.
 Besides, what's wrong with using ' '?
 
  serialize int a;
Builtin annotations' names are not reserved keywords, so this would be possible and ambiguous: string safe( string s ); safe void foo(); // what safe stands for?
They could be contextual keywords, just like the 'exit' in scope(exit). So we have AttributeIdentifier: any Identifier except 'safe', 'system', 'trusted', 'disable' and 'property'. (Alternatively, implement these 5 things using the CTFE method. )
Mar 21 2012