PROPOSAL: mixin candy
Ryan Bloomfield
_sir_maniacREMOVE_ME at yahoo.com
Sat Mar 29 02:35:22 PDT 2008
I'd like to propose an extension to templates:
I've given this a lot of thought, and carefully thought through each of the points. I hope I wrote this according to the NG conventions, and that it is clear enough to be understood. I'd like to know if this would be appropriate for D.
Currently with template mixins, any declarations that are legal in a template can be arbitrarily inserted into code. If statements or expressions need to be inserted in a function, the only option is to generate a string, and use mixin().
I propose the following language changes to reduce the need to handle strings when using mixins.
1. A mixin operator that can replace identifiers
Templates currently allow the creation of declarations using arguments to decide the types, and values. You cannot, however, choose the name declarations depending on the arguments, without some difficult to read string concatenation:
--------------------------------
template CreateVar(T, char[] name)
{
mixin(T.stringof ~ " " ~ name ~ ";"); // compiles fine, but hard to read
}
--------------------------------
I think, for the sake of readability, and simplicity templates should have the ability to name enclosed declarations using a single string argument.
template CreateVar(T, char[] name)
{
T mixin(name); // easier to read, less likely to make a mistake
}
A more complex example:
--------------------------------
template Property(char[] propName, alias member, Type)
{
Type mixin(propname)()
{
return mixin(member.stringof);
}
void mixin(propname)(Type val)
{
mixin(member.stringof) = val;
}
}
--------------------------------
I'm not sure if this is so, but using mixin() might cause parsing ambiguities, if it does, I propose a new token. For example, "~~(" and ")":
---------------------------------
template Property(char[] propName, alias member, Type)
{
Type ~~(propname)()
{
return ~~(member.stringof);
}
void ~~(propname)(Type val)
{
~~(member.stringof) = val;
}
}
-----------------------------------
This is easier to read, and the string concatenation operator seems appropriate, since the operator is telling the compiler to insert the string's value in it's place.
2. mixin declarations:
Template mixins only allow full declarations, the only way to add statements within a function, or mixin an expression within a statement is to work with strings that are passed to mixin():
--------------------------------
template MakeStructMember(alias structName, char[] structMember)
{
const char[] MakeStructReturn = structName.stringof
~ "." ~ structMember ~ ";";
}
...
int getMember()
{
return mixin(MakeStructMember!("thisStruct", "val"));
}
...
--------------------------------
With a mixin declaration, syntactically valid expressions and statements could be written without the need to build strings. Note that it uses a mixin to replace the identifiers, like I describe above:
--------------------------------
mixin StructMember(alias structName, char[] structMember)
{
structName. mixin(structMember)
}
...
int getMember(char[] structMember)()
{
return mixin(StructMember!(structName, structMember));
}
...
--------------------------------
Notice the mixin can accept expressions instead of the need for complete statements. I think this can be done without complicating the parser, because the contents of the mixin body need only have the same correctness a string has when passed to mixin(). All keywords, names, and operators must already be syntactically valid, with any names declared within the template handled as string arguments to mixin(). The text within the blocks can be thought of by the compiler as "data that needs to be stringized."
Combining incomplete statements with conditional compilation might pose additional problems. I think it can be solved, as long as incomplete statements are placed inside "{" and "}" blocks. Then the parser would would treat the items within the static if blocks as "data that needs to be stringized," like above.
A mixin instantiation would return a static string representing the code within the brackets, after conditional compilation, substitution of arguments(same rules as template arguments), and other mixin statements. The compiler should easily be able to convert them to a template:
---------------------------------
mixin StructMember(alias structName, char[] structMember)
{
structName.mixin(structMember)
}
---------------------------------
is functionally equivalent to:
---------------------------------
template StructMember(alias structName, char[] structMember)
{
const char[] StructMember = structName.stringof ~ "." ~ structMember;
}
---------------------------------
An example for an enumeration made with a tuple of strings, again using identifier replacement:
---------------------------------
mixin EnumItemList(LIST...)
{
static if(LIST.length > 1)
{
mixin(LIST[0]),
mixin(EnumItemList!(LIST[1..length])
}
else
{
mixin(LIST[0])
}
}
template EnumDeclaration(char[] name, VALUES...)
{
enum mixin(name)
{
mixin(EnumItemList!(VALUES))
}
}
---------------------------------
And the above converted to template definitions:
---------------------------------
template EnumItemList(LIST...)
{
static if(LIST.length > 1)
{
const char[] EnumItemList = LIST[0] ~ "," ~ "\n" ~
EnumItemList!(LIST[1..length]);
}
else
{
const char[] EnumItemList = LIST[0];
}
}
template EnumDeclaration(char[] name, VALUES...)
{
enum mixin(name)
{
mixin(EnumItemList!(VALUES))
}
}
---------------------------------
ryan
More information about the Digitalmars-d
mailing list