www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - UDAs on enum members

reply Tomer Filiba <tomerfiliba gmail.com> writes:
It would be really nice if I could put UDAs on enum members as 
well, e.g.,

enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName,
}

I can think of many reasons why that would be desired, but the 
concrete one is the following: I have an interchangeable data 
format, and my enum might gain members over time. I don't care 
about the value of the member, so I don't want to number them 
myself, but I can't control where users would choose to place the 
new member, so they might cause renumbering of existing members, 
breaking the interchangeable format.

So what I wanted is to assign each member a "short stable name" 
that would be used to serialize the value (using my own dump/load 
functions)... But I had to resort to a long, ugly switch 
statement, mapping members to their names and vice versa. The 
dumping function uses final-switch, so you won't forget to update 
it, but the loading one can't (it takes a string) so it would be 
easy to people to forget.

Given that UDAs can be used practically everywhere (including 
struct/union members), is there an objection to make them legal 
on enum members as well?

And while we're on the subject, why can't enums have methods? At 
the risk of sounding as if I like Java (I don't :) ), it's a 
really nice language feature. Back to our example:

enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName;

     string dump() {
         ...  // `this` is a value, not a ref here
     }
     static MyEnum load(string name) {
         ...
     }
}

Basically just allow a semicolon at the end of the members, after 
which methods could appear. Adding members or whatever else Java 
has is an overkill -- just use a struct for that. But instead of 
lots of dumpMyEnum(MyEnum e)/loadMyEnum(string s) pairs, you 
could write myMember.dump()/MyEnum.load(s)


-tomer
Jul 13 2016
next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Wed, 13 Jul 2016 11:57:21 +0000, Tomer Filiba wrote:
 And while we're on the subject, why can't enums have methods?
Why not use a free function? import std.stdio; enum Foo { bar, baz } void print(Foo f) { final switch(f) { case Foo.bar: writeln("bar"); break; case Foo.baz: writeln("baz"); break; } } void main() { Foo.bar.print(); }
 At the
 risk of sounding as if I like Java (I don't :) ), it's a really nice
 language feature.
Nothing wrong with liking things from Java.
Jul 13 2016
parent reply ketmar <ketmar ketmar.no-ip.org> writes:
On Wednesday, 13 July 2016 at 12:40:50 UTC, Chris Wright wrote:
 Why not use a free function?
'cause OP wants enum ctor to be called like this: MyEnum.create(somedata); 2OP: to create enum from string, you can use `std.conv.to`: enum E { A, B, C } E v = to!E("A"); still not a ctor, but i think that it looks clear enough.
Jul 13 2016
parent Tomer Filiba <tomerfiliba gmail.com> writes:
On Wednesday, 13 July 2016 at 12:54:19 UTC, ketmar wrote:
 2OP: to create enum from string, you can use `std.conv.to`:
   enum E { A, B, C }
   E v = to!E("A");
I obviously failed to convey the purpose. I don't need a string representation of the enum member, I want to be able to attach UDAs to members. The second part, about Java-like enums, was just an thought. Scratch that. Right now, I have this real code: enum Severity: ubyte { INFO, WARNING, MINOR, MAJOR, CRITICAL } notrace string severityToString(Severity sev) { final switch (sev) with (Severity) { case INFO: return "INF"; case WARNING: return "WRN"; case MINOR: return "MNR"; case MAJOR: return "MJR"; case CRITICAL: return "CRT"; } } notrace Severity stringToSeverity(string sev) { switch (sev) with (Severity) { case "INF": return INFO; case "WRN": return WARNING; case "MNR": return MINOR; case "MJR": return MAJOR; case "CRT": return CRITICAL; default: return WARNING; } } If I could attach UDAs to the enum members', I could just auto-generate that stuff. That's all I'm asking for. The purpose of having a short name is for compression -- this text is going to be stored a lot, and I don't want to store the value, because (a) I don't care what value is assigned to each and (b) users might add new members, anywhere in the enum, causing the values to be remapped. In other words, I don't want enum Severity: ubyte { INFO, MINOR, MAJOR, WARNING, CRITICAL } to break the data I already have -tomer
Jul 13 2016
prev sibling next sibling parent user1234 <user1234 user1234.au> writes:
On Wednesday, 13 July 2016 at 11:57:21 UTC, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as 
 well, e.g.,

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName,
 }

 I can think of many reasons why that would be desired, but the 
 concrete one is the following: I have an interchangeable data 
 format, and my enum might gain members over time. I don't care 
 about the value of the member, so I don't want to number them 
 myself, but I can't control where users would choose to place 
 the new member, so they might cause renumbering of existing 
 members, breaking the interchangeable format.

 So what I wanted is to assign each member a "short stable name" 
 that would be used to serialize the value (using my own 
 dump/load functions)... But I had to resort to a long, ugly 
 switch statement, mapping members to their names and vice 
 versa. The dumping function uses final-switch, so you won't 
 forget to update it, but the loading one can't (it takes a 
 string) so it would be easy to people to forget.

 Given that UDAs can be used practically everywhere (including 
 struct/union members), is there an objection to make them legal 
 on enum members as well?

 And while we're on the subject, why can't enums have methods? 
 At the risk of sounding as if I like Java (I don't :) ), it's a 
 really nice language feature. Back to our example:

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName;

     string dump() {
         ...  // `this` is a value, not a ref here
     }
     static MyEnum load(string name) {
         ...
     }
 }

 Basically just allow a semicolon at the end of the members, 
 after which methods could appear. Adding members or whatever 
 else Java has is an overkill -- just use a struct for that. But 
 instead of lots of dumpMyEnum(MyEnum e)/loadMyEnum(string s) 
 pairs, you could write myMember.dump()/MyEnum.load(s)


 -tomer
To the risk of not being usefull I wonder if what you want wouldn't be easier to obtain with a union and a bit of discipline. If you put only manifest constants inside a union you can have methods and UDAs. The union can be verified by a template. union Enum { int value; ("SOM") enum SomeMember = int(0); ("ANO") enum AnotherMemberWithAVeryLongName = int(1); } template validateEnum(T) { // check for unique-ness of each value // check other things. enum validateEnum = check(); } To the question of mapping, since each member has an unique value, the member value itself can be used as hash in an AA without producing clustering. This works well if values are integers or floats.
Jul 13 2016
prev sibling next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2016-07-13 13:57, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as well, e.g.,
I think it should be supported, if nothing else for "turtles all the way"/completeness. -- /Jacob Carlborg
Jul 14 2016
next sibling parent reply ZombineDev <petar.p.kirov gmail.com> writes:
On Thursday, 14 July 2016 at 07:05:08 UTC, Jacob Carlborg wrote:
 On 2016-07-13 13:57, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as 
 well, e.g.,
I think it should be supported, if nothing else for "turtles all the way"/completeness.
I agree. Another thing missing is parameter UDAs. There is [1] a pull request for that, but unfortunately it got stuck about a year ago. [1]: https://github.com/dlang/dmd/pull/4783
Jul 16 2016
parent =?UTF-8?Q?S=c3=b6nke_Ludwig?= <sludwig outerproduct.org> writes:
Am 16.07.2016 um 20:57 schrieb ZombineDev:
 On Thursday, 14 July 2016 at 07:05:08 UTC, Jacob Carlborg wrote:
 On 2016-07-13 13:57, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as well,
 e.g.,
I think it should be supported, if nothing else for "turtles all the way"/completeness.
I agree. Another thing missing is parameter UDAs. There is [1] a pull request for that, but unfortunately it got stuck about a year ago. [1]: https://github.com/dlang/dmd/pull/4783
+1 for both, they would be very useful. For example vibe.d currently requires passing the parameter name as an argument to the UDA to work around the lack of support for UDAs on parameters. Kind of related, the "deprecated" attribute would also be important to have for enum members [1]. I still have a bunch of enum members that were supposed to be removed/replaced years ago, but there is no way to implement a deprecation process. [1]: https://issues.dlang.org/show_bug.cgi?id=9395
Jul 16 2016
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 07/14/2016 12:05 AM, Jacob Carlborg wrote:
 On 2016-07-13 13:57, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as well,
 e.g.,
I think it should be supported, if nothing else for "turtles all the way"/completeness.
One more +1... To those who know the compiler internals, is there a reason why UDA are not applied to all turtles? (I guess the answer is not that simple. :) ) Ali
Jul 17 2016
next sibling parent ketmar <ketmar ketmar.no-ip.org> writes:
On Sunday, 17 July 2016 at 16:07:13 UTC, Ali Çehreli wrote:
 To those who know the compiler internals, is there a reason why 
 UDA are not applied to all turtles?
mostly 'cause parser doesn't support that.
Jul 17 2016
prev sibling parent reply Jonathan M Davis via Digitalmars-d <digitalmars-d puremagic.com> writes:
On Sunday, July 17, 2016 09:07:13 Ali Çehreli via Digitalmars-d wrote:
 To those who know the compiler internals, is there a reason why UDA are
 not applied to all turtles? (I guess the answer is not that simple. :) )
Mabye some of them are actually tortoises? ;) D's features rarely seem to be implemented as "turtles all the way down" to begin with. That's usually something that comes later after folks complain about something that they think is inconsistent. And it's sometimes disputable as to whether something counts as being "turtles all the way down" or whether it would actually be a good idea to _not_ do "turtles all the way down" (for instance, some of the way that attributes are applied such that you're allowed to put them on all kinds of things where they don't apply but don't get an error is arguably a case of "turtles all the way down," but it's also quite problematic at least some of the time). In this particular case, I have no idea whether it would be a good idea or not, but it is a bit similar to applying UDAs to variables, which would be rather odd (though per the OP, it sounds like it's legal to put them on member variables; I don't have any experience with UDAs though, so I'm not sure what their restrictions are exactly). And if you can put UDAs on enum members the way that the OP is looking to do, that immediately poses the question of whether something like enum MyUDA foo = "hello world"; should be legal, and if we do that, we're pretty much at the point that any variable should be allowed to have a UDA, which seems pointless and overcomplicated to me - particularly with regards to local variables. I expect that if there's a good enough, practical argument to be made, then we could end up with UDAs on enum members, but that argument is going to have to be made such that Walter and Andrei are convinced, which may or may not be easy. But anyone who feels strongly about this should consider putting together a DIP. - Jonathan M Davis
Jul 17 2016
parent Jacob Carlborg <doob me.com> writes:
On 2016-07-17 19:01, Jonathan M Davis via Digitalmars-d wrote:

 Mabye some of them are actually tortoises? ;)

 D's features rarely seem to be implemented as "turtles all the way down" to
 begin with. That's usually something that comes later after folks complain
 about something that they think is inconsistent. And it's sometimes
 disputable as to whether something counts as being "turtles all the way
 down" or whether it would actually be a good idea to _not_ do "turtles all
 the way down" (for instance, some of the way that attributes are applied
 such that you're allowed to put them on all kinds of things where they don't
 apply but don't get an error is arguably a case of "turtles all the way
 down," but it's also quite problematic at least some of the time).

 In this particular case, I have no idea whether it would be a good idea or
 not, but it is a bit similar to applying UDAs to variables, which would be
 rather odd (though per the OP, it sounds like it's legal to put them on
 member variables;
I'm using UDA's on instance variables in my serialization library: class Foo { int a; nonSerialized int b; } I could also imagine more use cases for a serialization library: class Foo { name("a") int _a; verison_(2) int b; } Other use cases would be to communicate to other applications. In Objective-C #defines are used in the code to indicate which instance variables and functions/actions can be connected in Interface Builder. In D, UDA's would be a perfect fit for that: class Foo { IBOutlet NSButton button; IBAction void onClick(NSButton sender) {} }
 I don't have any experience with UDAs though, so I'm not
 sure what their restrictions are exactly). And if you can put UDAs on enum
 members the way that the OP is looking to do, that immediately poses the
 question of whether something like

 enum  MyUDA foo = "hello world";
I don't see why this shouldn't be possible.
 should be legal, and if we do that, we're pretty much at the point that any
 variable should be allowed to have a UDA, which seems pointless and
 overcomplicated to me - particularly with regards to local variables.
I don't see a reason not allowing UDA's on local variables either. You might say that it's not possible to access local variables, true, but the compiler might not be the only tool that analyzes the source code. I just suggested in another thread [1] that a UDA could be used on a local variable and then be interpreted by an external tool, I didn't even think that UDA's could not be used on local variables. Going back to the Objective-C example with IBOutlet/IBAction. IBOutlet is a #define which expands to nothing and IBAction expands to "void". Clang recognizes these #defines [2], not because the compiler needs to but because Clang is used to build other tools that analyzes source code, in this case Xcode/Interface Builder.
 I expect that if there's a good enough, practical argument to be made, then we
 could end up with UDAs on enum members, but that argument is going to have
 to be made such that Walter and Andrei are convinced, which may or may not
 be easy. But anyone who feels strongly about this should consider putting
 together a DIP.
A naive implementation could allow to put attributes on all declarations, but that would then allow meaningless code like adding "private" to a locale variable. But when it comes to UDA's, a single UDA doesn't have a semantic meaning itself, like "private" does. For a programming language to be useful it needs to be designed in a way that allows the users to implement things that the designers have not thought of. Adding artificial restrictions because the designers can not come up with a use case goes very much against this. [1] http://forum.dlang.org/post/nmg142$2ji1$1 digitalmars.com [2] http://clang.llvm.org/doxygen/group__CINDEX.html#gaaccc432245b4cd9f2d470913f9ef0013 -- /Jacob Carlborg
Jul 17 2016
prev sibling next sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 7/13/2016 4:57 AM, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as well, e.g.,

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName,
 }
Not a bad idea. It's been asked for before.
 And while we're on the subject, why can't enums have methods? At the risk of
 sounding as if I like Java (I don't :) ), it's a really nice language feature.
 Back to our example:

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName;

     string dump() {
         ...  // `this` is a value, not a ref here
     }
     static MyEnum load(string name) {
         ...
     }
 }

 Basically just allow a semicolon at the end of the members, after which methods
 could appear. Adding members or whatever else Java has is an overkill -- just
 use a struct for that. But instead of lots of dumpMyEnum(MyEnum
 e)/loadMyEnum(string s) pairs, you could write myMember.dump()/MyEnum.load(s)
struct MyEnum { ("SOM") enum SomeMember = 0; ("ANO") enum AnotherMemberWithAVeryLongName = 1; string dump() { ... // `this` is a value, not a ref here } static MyEnum load(string name) { ... } } Not as nice, but gets the job done. But adding a new aggregate type comes with a lot of baggage. I'm not convinced the complexity is worth the benefit, which seems minor.
Jul 17 2016
next sibling parent Basile B. <b2.temp everywhere.net> writes:
On Sunday, 17 July 2016 at 20:51:42 UTC, Walter Bright wrote:
 On 7/13/2016 4:57 AM, Tomer Filiba wrote:
 struct MyEnum {
      ("SOM") enum SomeMember = 0;
      ("ANO") enum AnotherMemberWithAVeryLongName = 1;

     string dump() {
         ...  // `this` is a value, not a ref here
     }
     static MyEnum load(string name) {
         ...
     }
 }

 Not as nice, but gets the job done. But adding a new aggregate 
 type comes with a lot of baggage. I'm not convinced the 
 complexity is worth the benefit, which seems minor.
With a union it's ok, see the sugestion here https://forum.dlang.org/post/kmqmqxskccaceurutgzq forum.dlang.org members are not part of the aggregate. The size is then just equal to the common type, used as variable to make bit op like with a regular enum. I mean that the OP is asking for a really minor feature while he could solve the problem himself, in case he really needs it.
Jul 18 2016
prev sibling parent EntangledQuanta <EQ universe.com> writes:
On Sunday, 17 July 2016 at 20:51:42 UTC, Walter Bright wrote:
 On 7/13/2016 4:57 AM, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as 
 well, e.g.,

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName,
 }
Not a bad idea. It's been asked for before.
 And while we're on the subject, why can't enums have methods? 
 At the risk of
 sounding as if I like Java (I don't :) ), it's a really nice 
 language feature.
 Back to our example:

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName;

     string dump() {
         ...  // `this` is a value, not a ref here
     }
     static MyEnum load(string name) {
         ...
     }
 }

 Basically just allow a semicolon at the end of the members, 
 after which methods
 could appear. Adding members or whatever else Java has is an 
 overkill -- just
 use a struct for that. But instead of lots of dumpMyEnum(MyEnum
 e)/loadMyEnum(string s) pairs, you could write 
 myMember.dump()/MyEnum.load(s)
struct MyEnum { ("SOM") enum SomeMember = 0; ("ANO") enum AnotherMemberWithAVeryLongName = 1; string dump() { ... // `this` is a value, not a ref here } static MyEnum load(string name) { ... } } Not as nice, but gets the job done. But adding a new aggregate type comes with a lot of baggage. I'm not convinced the complexity is worth the benefit, which seems minor.
If it's a simple rewrite rule then there is no complexity. I don't see how adding UDA's to enums is a new aggregate type(it's still an enum). The problem with such rewrites by the user is that the structs do not behave as enums and so all the mechanisms that enums have won't work. This creates problems, bugs, kludges, etc. Maybe the problem is that the implementation of UDA's is not expressive enough to modify in a simple way? So fixing the code to make it do what it should have done in the first place is where the complexity lies? Having UDA's are as useful as having them. How do you know how useful they can be until it is done? I have several perfectly valid use cases for them, and the hacks are 10x much work and may break some code that depends them actually being enum's. You should realize that what you see as minor is not necessarily minor and what you think is not beneficial is only your relative opinion about yourself. What would be more helpful is for you to state what criteria you would need to actually put in the effort to implement the new features rather than stating opinions that only shut down progress. Just remember there are at least 10's of thousands of D users and your experiences with writing D code is only one small aspect. Many D users that come from different backgrounds will have different understandings and different approaches to how they will write *their* code and what they expect. While you owe them nothing, it would be nice to at least acknowledge their view as relevant. If something is a theoretical contradiction, that is one thing, and it must be proved so... but is something is simply not done out of priority, laziness, convenience, etc then that is another. If dmd is properly written then the complexity of adding new features should be compartmentalized and relatively localized. While one can never predict the ramifications of adding new features(both good and bad), I think this is why there are experimental versions of dmd? New features should not be looked upon with disdain but be welcomed. The potential issues and complexity that seem to be brought up can be mitigated with the proper approaches.
Aug 31 2017
prev sibling parent apz28 <home home.com> writes:
On Wednesday, 13 July 2016 at 11:57:21 UTC, Tomer Filiba wrote:
 It would be really nice if I could put UDAs on enum members as 
 well, e.g.,

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName,
 }

 I can think of many reasons why that would be desired, but the 
 concrete one is the following: I have an interchangeable data 
 format, and my enum might gain members over time. I don't care 
 about the value of the member, so I don't want to number them 
 myself, but I can't control where users would choose to place 
 the new member, so they might cause renumbering of existing 
 members, breaking the interchangeable format.

 So what I wanted is to assign each member a "short stable name" 
 that would be used to serialize the value (using my own 
 dump/load functions)... But I had to resort to a long, ugly 
 switch statement, mapping members to their names and vice 
 versa. The dumping function uses final-switch, so you won't 
 forget to update it, but the loading one can't (it takes a 
 string) so it would be easy to people to forget.

 Given that UDAs can be used practically everywhere (including 
 struct/union members), is there an objection to make them legal 
 on enum members as well?

 And while we're on the subject, why can't enums have methods? 
 At the risk of sounding as if I like Java (I don't :) ), it's a 
 really nice language feature. Back to our example:

 enum MyEnum {
      ("SOM") SomeMember,
      ("ANO") AnotherMemberWithAVeryLongName;

     string dump() {
         ...  // `this` is a value, not a ref here
     }
     static MyEnum load(string name) {
         ...
     }
 }

 Basically just allow a semicolon at the end of the members, 
 after which methods could appear. Adding members or whatever 
 else Java has is an overkill -- just use a struct for that. But 
 instead of lots of dumpMyEnum(MyEnum e)/loadMyEnum(string s) 
 pairs, you could write myMember.dump()/MyEnum.load(s)


 -tomer
Here how I implement it struct EnumArray(E, V) { nothrow safe: public: struct Entry { E e; V v; } private: enum isEntryType(T) = is(Entry == T); enum size = EnumMembers!E.length; V[size] _values; public: this(T...)(T aValues) if (allSatisfy!(isEntryType, T)) { foreach (ref Entry i; aValues) _values[i.e] = i.v; } V opIndex(E aEnum) const { return _values[aEnum]; } V opIndexAssign(V aValue, E aEnum) { return _values[aEnum] = aValue; } V opDispatch(string aEnumName)() const { import std.conv : to; enum e = aEnumName.to!E; return _values[e]; } version (none) V opDispatch(string aEnumName)(V aValue) { import std.conv : to; enum e = aEnumName.to!E; return _values[e] = aValue; } E getEnum(V aValue, E aDefault = E.min) { foreach (i; EnumMembers!E) { if (_values[i] == aValue) return i; } return aDefault; } property: size_t length() const { return size; } } unittest // EnumArray { enum EnumTest { one, two, max } alias EnumTestInt = EnumArray!(EnumTest, int); EnumTestInt testInt = EnumTestInt( EnumTestInt.Entry(EnumTest.one, 1), EnumTestInt.Entry(EnumTest.two, 2), EnumTestInt.Entry(EnumTest.max, int.max) ); assert(testInt.one == 1); assert(testInt.two == 2); assert(testInt.max == int.max); assert(testInt[EnumTest.one] == 1); assert(testInt[EnumTest.two] == 2); assert(testInt[EnumTest.max] == int.max); assert(testInt.getEnum(1) == EnumTest.one); assert(testInt.getEnum(2) == EnumTest.two); assert(testInt.getEnum(int.max) == EnumTest.max); assert(testInt.getEnum(3) == EnumTest.one); // Unknown -> return default min alias EnumTestString = EnumArray!(EnumTest, string); EnumTestString testString = EnumTestString( EnumTestString.Entry(EnumTest.one, "1"), EnumTestString.Entry(EnumTest.two, "2"), EnumTestString.Entry(EnumTest.max, "int.max") ); assert(testString[EnumTest.one] == "1"); assert(testString[EnumTest.two] == "2"); assert(testString[EnumTest.max] == "int.max"); assert(testString.getEnum("1") == EnumTest.one); assert(testString.getEnum("2") == EnumTest.two); assert(testString.getEnum("int.max") == EnumTest.max); assert(testString.getEnum("3") == EnumTest.one); // Unknown -> return default min } Pham
Aug 31 2017