Thread overview | |||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
February 19, 2007 Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Hello, Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible. Reflective enums have two useful properties that don't have normal enums: - their label is printable - it's possible to do a foreach on all the possible values. In my opinion, for the reflective enums, the following constraint should apply: - they should 'look like' normal enums as much as possible. - then the printable label feature should be as simple to use as possible: this feature will probably be used much more often than the foreach feature. For the API, the functions needed are: a- Reflective Enum(REnum) list definition. b. REnum variable definition. c. Reference to an REnum label. d. Print the label corresponding to the value of an REnum variable. e. Iterate over all possible value in an REnum list. f. Iterate over all possible labels in an REnum list (I've added this one by symmetry). (a) Takes a name and either define (1) an enum of this name plus some functions to print enum labels and iterate over it, or alternatively as in the original code it can return a 'thing' (struct for example as it in the initial implementation) (2) which contain the enum and functions. I'm not sure which is best. (e) and (f) reminds me of associate arrays, maybe a function to generate an associative array from the enum list would be enough.. I'm trying to create functions to (e) and (f), but I have trouble to make my code work currently: debugging template code is not very easy.. renoX |
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to renoX | renoX wrote: > Hello, > Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible. > Reflective enums have two useful properties that don't have normal enums: > - their label is printable > - it's possible to do a foreach on all the possible values. What about this one: - it's possible to do a slice and get a new enum with a subset of the original enum values (and labels) You could do something like this: enum Weekday {MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY} foreach (day; Weekday[MONDAY..FRIDAY]) { ... } -- Michiel |
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to renoX | renoX wrote: > Hello, > Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible. > Reflective enums have two useful properties that don't have normal enums: > - their label is printable > - it's possible to do a foreach on all the possible values. By the way, should this not be in the core language? -- Michiel |
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michiel | Michiel Wrote: > renoX wrote: > > > Hello, > > Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible. > > Reflective enums have two useful properties that don't have normal enums: > > - their label is printable > > - it's possible to do a foreach on all the possible values. > > By the way, should this not be in the core language? Well I think that Ada does it in the core language (but I think that they are not printable), it is able to define enum as an index array as you suggest in your second email. As the more I look at D, the more I find it similar to Ada, it is perhaps a good idea to make them those enhanced enum in the core language, and as an improvement to Ada those enum should be printable (maybe as an option to save memory) . In the meantime, my trial is below, but it doesn't work when there are two enums: the two toString conflicts, even though they should not because their parameter is a different enum type. Should I report this as a bug? renoX import std.stdio; import std.metastrings; template Find(char[] A, char[] B) { static if (A.length < B.length) { const int Find = -1; } else static if (A[0..B.length] == B) { const int Find = 0; } else static if (-1 == Find!(A[1..$], B)) { const int Find = -1; } else { const int Find = 1 + Find!(A[1..$], B); } } template SplitFirst(char[] A, char[] B) { const int Location = Find!(A, B); static if (Location == -1) { const char[] First = A; const char[] Rest = ""; } else { const char[] First = A[0..Location]; const char[] Rest = A[Location+B.length..$]; } } template ChompSpaces(char[] A) { static if (A.length) { static if (A[0] == ' ') { alias ChompSpaces!(A[1..$]) ChompSpaces; } else static if (A[$-1] == ' ') { alias ChompSpaces!(A[0..$-1]) ChompSpaces; } else { alias A ChompSpaces; } } else { const char[] ChompSpaces = ""; } } template SplitChomp(char[] A, char[] B) { alias ChompSpaces!(SplitFirst!(A, B).First) First; alias ChompSpaces!(SplitFirst!(A, B).Rest) Rest; } template EnumName(char[] A) { alias SplitChomp!(SplitFirst!(A, ",").First, "=").First EnumName; } template EnumAssign(char[] A) { alias SplitChomp!(SplitFirst!(A, ",").First, "=").Rest EnumAssign; } template EnumValue(char[] A, int i) { static if (EnumAssign!(A) == "") { const int EnumValue = i; } else { const int EnumValue = mixin(ParseInteger!(EnumAssign!(A)).value); } } template EnumListName(char[] A) { alias SplitChomp!(A, "{").First EnumListName; } template EnumBody(char[] A) { alias SplitChomp!(SplitFirst!(A, "}").First, "{").Rest EnumBody; } template BuildOneEnum(char[] A, int i) { const char[] BuildOneEnum = "const int "~EnumName!(A)~" = "~ ToString!(EnumValue!(A, i))~";\n"; } template BuildEnums(char[] A, int i) { static if (SplitChomp!(A, ",").Rest.length == 0) { const char[] BuildEnums = BuildOneEnum!(A, EnumValue!(A, i)); } else { const char[] BuildEnums = BuildOneEnum!(SplitChomp!(A, ",").First, EnumValue!(A, i)) ~ BuildEnums!(SplitChomp!(A, ",").Rest, EnumValue!(A, i)+1); } } template BuildOneCase(char[] A, int i, bool full) { static if (!full) { const char[] BuildOneCase = "case "~ToString!(EnumValue!(A, i))~ ": return \""~EnumName!(A)~"\";\n"; } else { const char[] BuildOneCase = "case "~ToString!(EnumValue!(A, i))~ ": return \""~EnumName!(A)~"("~ToString!(EnumValue!(A, i))~")\";\n"; } } template BuildEnumCases(char[] A, int i, bool full) { static if (SplitChomp!(A, ",").Rest.length == 0) { const char[] BuildEnumCases = BuildOneCase!(A, EnumValue!(A, i), full); } else { const char[] BuildEnumCases = BuildOneCase!(SplitChomp!(A, ",").First, EnumValue!(A, i), full) ~ BuildEnumCases!(SplitChomp!(A, ",").Rest, EnumValue!(A, i)+1,full); } } template BuildEnumSwitch(char[] A, int i, bool full) { const char[] BuildEnumSwitch = "switch(x) {"~ BuildEnumCases!(A, i, full) ~ "default: "~ " throw new Exception(\"enumeration out of range\");" "}"; } template BuildEnumElem(char[] A, char[]enumListName, int i, int ekv) { static if (ekv == 0) { const char[] BuildEnumElem = enumListName~"."~EnumName!(A)~", "; } ; static if (ekv == 1) { const char[] BuildEnumElem = "\""~EnumName!(A)~"\", "; }; static if (ekv == 2) { const char[] BuildEnumElem = ToString!(EnumValue!(A, i))~", "; } } // build the list of key if key is true, the list of values if key is false template BuildEnumList(char[] A, char[]enumListName, int i, int ekv) { static if (SplitChomp!(A, ",").Rest.length == 0) { const char[] BuildEnumList = BuildEnumElem!(A, enumListName, EnumValue!(A, i), ekv); } else { const char[] BuildEnumList = BuildEnumElem!(SplitChomp!(A, ",").First, enumListName, EnumValue!(A, i), ekv)~ BuildEnumList!(SplitChomp!(A, ",").Rest, enumListName, EnumValue!(A, i)+1, ekv); } } //to be mature it should parse also the EnumBaseType template DefEnum(char[] enum_str) { mixin("enum "~enum_str~";"); mixin( "static char[] toString("~EnumListName!(enum_str)~" val) { int x = val; "~ BuildEnumSwitch!(EnumBody!(enum_str), 0, false)~ " }" ); mixin( "static char[] toFullString("~EnumListName!(enum_str)~" val) { int x = val; "~ BuildEnumSwitch!(EnumBody!(enum_str), 0, true)~ " }" ); mixin( "const "~EnumListName!(enum_str)~"[] "~EnumListName!(enum_str)~"_elem ="~ " [ "~ BuildEnumList!(EnumBody!(enum_str), EnumListName!(enum_str), 0, 0)~ " ];" ); mixin( "const char[][]"~EnumListName!(enum_str)~"_keys = [ "~ BuildEnumList!(EnumBody!(enum_str), EnumListName!(enum_str), 0, 1)~ " ];" ); mixin( "const int[]"~EnumListName!(enum_str)~"_values = [ "~ BuildEnumList!(EnumBody!(enum_str), EnumListName!(enum_str), 0, 2)~ " ];" ); } int main(char[][] args) { // mixin DefEnum!("LEnum2 {other=2, cont, end2=15, ps}"); mixin DefEnum!("LEnum {start, middle, end=10, ps, pps,}"); LEnum s = LEnum.start; writefln("s is %s, with name %s full name '%s'\n", s, toString(s), toFullString(s)); foreach(i,v; LEnum_elem) { writefln("Enum %d has name=%s value %d", i, toString(v), v); } // LEnum2 o = LEnum2.other; // writefln("o is %s, with name %s full name '%s'\n", o, toString(o), toFullString(o)); foreach(i,v; LEnum_keys) { writefln("Enum %d has name=%s and value %d", i, v, LEnum_values[i]); } foreach(i,v; LEnum_values) { writefln("Enum %d has name=%s value %d", i, toString(cast(LEnum)v), v); } return 0; } |
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to renoX | renoX wrote:
> Hello,
> Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible.
> Reflective enums have two useful properties that don't have normal enums:
> - their label is printable
> - it's possible to do a foreach on all the possible values.
>
> In my opinion, for the reflective enums, the following constraint should apply:
> - they should 'look like' normal enums as much as possible.
Why? There is so much more freedom now, why limit ourselves. I think enums of arbitrary types should be allowed, including arrays, hashes, and user-defined types.
An enumerated type is nothing but a closed set of named values. The names are always strings; the values can be of any (uniform) type.
Andrei
|
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to renoX | renoX wrote:
> Michiel Wrote:
>
>> renoX wrote:
>>
>>> Hello,
>>> Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible.
>>> Reflective enums have two useful properties that don't have normal enums:
>>> - their label is printable
>>> - it's possible to do a foreach on all the possible values.
>> By the way, should this not be in the core language?
>
> Well I think that Ada does it in the core language (but I think that they are not printable), it is able to define enum as an index array as you suggest in your second email.
> As the more I look at D, the more I find it similar to Ada, it is perhaps a good idea to make them those enhanced enum in the core language, and as an improvement to Ada those enum should be printable (maybe as an option to save memory) .
On the contrary, I think it would be great if it were _not_ in the core language, and I'd be happy if a library enum was so good, it would cause deprecation of the built-in one.
Andrei
|
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | Andrei Alexandrescu (See Website For Email) wrote: >> Well I think that Ada does it in the core language (but I think that >> they are not printable), it is able to define enum as an index array >> as you suggest in your second email. >> As the more I look at D, the more I find it similar to Ada, it is >> perhaps a good idea to make them those enhanced enum in the core >> language, and as an improvement to Ada those enum should be printable >> (maybe as an option to save memory) . > > On the contrary, I think it would be great if it were _not_ in the core language, and I'd be happy if a library enum was so good, it would cause deprecation of the built-in one. That's a strange thing to hope for. There are advantages to having a feature in the core language instead of a library. Several of them are summed up on the website. In this case: * The syntax will be nice and clean. * The compiler knows exactly what you mean and can optimize accordingly. Those are not true of library implementations. -- Michiel |
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu (See Website For Email) | Andrei Alexandrescu (See Website For Email) a écrit : > renoX wrote: >> Hello, >> Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible. >> Reflective enums have two useful properties that don't have normal enums: >> - their label is printable >> - it's possible to do a foreach on all the possible values. >> >> In my opinion, for the reflective enums, the following constraint should apply: >> - they should 'look like' normal enums as much as possible. > > Why? Because that way a programmer used to normal enums can use reflective enums nearly identically. > There is so much more freedom now, why limit ourselves. I think enums of arbitrary types should be allowed, including arrays, hashes, and user-defined types. This would be a possible extension, but I wonder if it wouldn't be a bit redundant with associative array.. And I think that with the current implementation, it's would be very difficult,as we do our own parsing which is of course quite limited. Mmm, with this extension, my idea of reusing the built-in enum type doesn't work.. Kevin Bealer's implementation would work.. I'll try to tweak it to see if it's possible to make it feel more enum-like. > An enumerated type is nothing but a closed set of named values. The names are always strings; the values can be of any (uniform) type. The names are not arbitrary strings but names/symbol: I wonder if a basic type 'symbol' would be interesting.. renoX > > > Andrei |
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to renoX | renoX wrote:
> Hello,
> Kevin Bealer has started an implementation (two implementations in fact!) of reflective enums, and I wanted to discuss the use case and the design of this feature to make it as useful as possible.
> Reflective enums have two useful properties that don't have normal enums:
> - their label is printable
> - it's possible to do a foreach on all the possible values.
>
> In my opinion, for the reflective enums, the following constraint should apply:
> - they should 'look like' normal enums as much as possible.
> - then the printable label feature should be as simple to use as possible: this feature will probably be used much more often than the foreach feature.
>
> For the API, the functions needed are:
> a- Reflective Enum(REnum) list definition.
> b. REnum variable definition.
> c. Reference to an REnum label.
> d. Print the label corresponding to the value of an REnum variable.
> e. Iterate over all possible value in an REnum list.
> f. Iterate over all possible labels in an REnum list (I've added this one by symmetry).
>
> (a) Takes a name and either define (1) an enum of this name plus some functions to print enum labels and iterate over it, or alternatively as in the original code it can return a 'thing' (struct for example as it in the initial implementation) (2) which contain the enum and functions.
> I'm not sure which is best.
>
> (e) and (f) reminds me of associate arrays, maybe a function to generate an associative array from the enum list would be enough..
>
> I'm trying to create functions to (e) and (f), but I have trouble to make my code work currently: debugging template code is not very easy..
>
> renoX
>
Your comment about e and f matches something I've been thinking. The action of iterating over the enum and in fact looking up the string are
essentially similar to an associative array. Imagine these two definitions in regular D:
enum Xyz_t {
x = 10,
y = 100,
z = 201
};
char[][Xyz_t] Xyz_t_lookup;
lookup[x] = "x";
lookup[y] = "y";
lookup[z] = "z";
Xyz_t[] Xyz_t_keys = Xyz_t_lookup.keys;
char[][] Xyz_t_labels = ["x", "y", "z"];
Now, I can do all the above operations with these definitions. We could build a reflective enum that converted to this definition. The main thing that I would change is that instead of a regular associative array, we can define a compile-time associative array. We can be more efficient in a CTAA than an AA because all values are known at compile time -- we convert all lookups to switches. It would look like this:
// given types T1 and T2
// constructed like this:
// CTAA!(int, char[], "10, \"x\", 100, \"y\", 201, \"z\"");
struct CTAA(T1, T2, E... values) {
const T1[] keys = [10,100,201];
const T2[] values = ["x","y","z"];
int opApply(int delegate(T1 k, T2 label) dg)
{
int rv;
rv = dg(10, "x"); if (rv) return rv;
rv = dg(100, "y"); if (rv) return rv;
rv = dg(201, "z"); if (rv) return rv;
return 0;
}
T1 getKey(T2 label)
{
switch(label) {
case "x": return 10;
case "y": return 100;
case "z": return 201;
default: throw new Exception("unknown label");
}
}
T2 getLabel(T1 key)
{
switch(label) {
case 10: return "x";
case 100: return "y";
case 201: return "z";
default: throw new Exception("unknown enum");
}
}
}
Note that the "\"x\"" stuff is a bit awkward but the reflective enum would be a wrapper around the AA; since it knows that it's 'strings' are just C identifiers, it can omit the escaped quotes in its initializer.
Also, since the CTAA is not a real AA there is no reason not to provide the translation in both directions as shown above -- it's not really an AA just a quick way to define a couple of handy switch statements.
In principle, switch/case should be more or less always be faster than actual AAs.
Kevin
|
February 19, 2007 Re: Design of reflective enums | ||||
---|---|---|---|---|
| ||||
Posted in reply to Michiel | Michiel wrote:
> Andrei Alexandrescu (See Website For Email) wrote:
>
>>> Well I think that Ada does it in the core language (but I think that
>>> they are not printable), it is able to define enum as an index array
>>> as you suggest in your second email.
>>> As the more I look at D, the more I find it similar to Ada, it is
>>> perhaps a good idea to make them those enhanced enum in the core
>>> language, and as an improvement to Ada those enum should be printable
>>> (maybe as an option to save memory) .
>> On the contrary, I think it would be great if it were _not_ in the core
>> language, and I'd be happy if a library enum was so good, it would cause
>> deprecation of the built-in one.
>
> That's a strange thing to hope for. There are advantages to having a
> feature in the core language instead of a library. Several of them are
> summed up on the website. In this case:
>
> * The syntax will be nice and clean.
> * The compiler knows exactly what you mean and can optimize accordingly.
>
> Those are not true of library implementations.
These particular advantages don't come forward in the case of enums. Core language features also have a lot of disadvantages, such as increasing the size of the language's definition, making the feature inherently rigid and hard to evolve, and putting the onus on the language implementer.
I see how strings can be meaningfully made part of the language, but instead of beefing up enums to the behemoth they are in Java, it's much more attractive to allowing libraries to do that.
Andrei
|
Copyright © 1999-2021 by the D Language Foundation