www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 9924] New: Handy enum accessors

reply d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924

           Summary: Handy enum accessors
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: Phobos
        AssignedTo: nobody puremagic.com
        ReportedBy: bearophile_hugs eml.cc



To better use enums I suggest to add to Phobos four small templates/functions
like this (similar things are builtins in Ada):


import std.conv: text;
import std.traits: EnumMembers;

template FirstMember(E) if (is(E == enum)) {
    enum FirstMember = EnumMembers!E[0];
}

template LastMember(E) if (is(E == enum)) {
    enum LastMember = EnumMembers!E[$ - 1];
}

E predMember(E)(E e) /*pure nothrow*/ if (is(E == enum))
in {
    assert(e != FirstMember!E);
} body {
    switch (e) {
        foreach (i, e2; EnumMembers!E[1 .. $])
            mixin("case E." ~ text(cast(E)e2) ~ ": return E."
                  ~ text(cast(E)(EnumMembers!E[i])) ~ ";");
        default: assert(0);
    }
}

E nextMember(E)(E e) /*pure nothrow*/ if (is(E == enum))
in {
    assert(e != LastMember!E);
} body {
    switch (e) {
        foreach (i, e2; EnumMembers!E[0 .. $ - 1])
            mixin("case E." ~ text(cast(E)e2) ~ ": return E."
                  ~ text(cast(E)(EnumMembers!E[i + 1])) ~ ";");
        default: assert(0);
    }
}

void main() { // Demo -----------------------
    import std.stdio;

    enum Choice { rock, paper, scissors }

    static Choice whatBeats(in Choice ch) /*pure nothrow*/ {
        if (ch == LastMember!Choice)
            return FirstMember!Choice;
        else
            return nextMember(ch);
    }

    foreach (e; [EnumMembers!Choice])
        writeln(e, " ", whatBeats(e));
    writeln(predMember(Choice.paper));
    writeln(predMember(Choice.scissors));
    writeln(nextMember(Choice.rock));
    writeln(nextMember(Choice.paper));
}

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 11 2013
next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924


Walter Bright <bugzilla digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla digitalmars.com



18:04:42 PDT ---
I don't see much need for FirstMember and LastMember, just as I don't see a
need for:

E FirstElement(E)(E[] a) { return a[0]; }

I believe such functions are trivia.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 11 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924





 I don't see much need for FirstMember and LastMember, just as I don't see a
 need for:
 
 E FirstElement(E)(E[] a) { return a[0]; }
 
 I believe such functions are trivia.
Maybe you are right, I am not sure. But note FirstElement is present in Phobos, it's named std.array.front: property ref T front(T)(T[] a) if (!isNarrowString!(T[]) && !is(T[] == void[])) { assert(a.length, "Attempting to fetch the front of an empty array of " ~ T.stringof); return a[0]; } -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Apr 11 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924




If you remove FirstMember and LastMember the whatBeats() function:


    static Choice whatBeats(in Choice ch) /*pure nothrow*/ {
        if (ch == LastMember!Choice)
            return FirstMember!Choice;
        else
            return nextMember(ch);
    }


Becomes:

    static Choice whatBeats(in Choice ch) /*pure nothrow*/ {
        if (ch == EnumMembers!Choice[$ - 1])
            return EnumMembers!Choice[0];
        else
            return nextMember(ch);
    }


It's more noisy, and visually it's a little less easy to tell it's correct.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 11 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924




19:37:02 PDT ---

 But note FirstElement is present in Phobos, it's named std.array.front:
front() is there to support ranges, it is not a shorthand for [0]. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Apr 11 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924




19:47:44 PDT ---
For similar reasons, I don't see a compelling case for nextMember or
prevMember, either. I expect that an algorithm needing the next or previous
member would be looping over EnumMembers!E[i] anyway.

Such functions do not make code clearer, they obfuscate it behind trivia. The
user wastes time wondering "should I use first(), or [0]? Why are there both?
Is there a difference?" The documentation fills up with this pointless
ephemera.

A well designed interface should have a *minimum* of concepts and methods. They
should ideally all be orthogonal, with zero overlap.

I don't know Ada, but I suspect it has these methods because it does not have
the [i] way of getting at enum members.

-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 11 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924






 front() is there to support ranges, it is not a shorthand for [0].
I don't agree. In the last year and half I have written two times by mistake: return items[$]; instead of: return items[$ - 1]; Now I usully use this, and avoid the problem, and it works even if later items becomes a range: return items.back; -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Apr 12 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924


bearophile_hugs eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
             Status|NEW                         |RESOLVED
         Resolution|                            |FIXED




 For similar reasons, I don't see a compelling case for nextMember or
 prevMember, either.
OK, I close this ER down. -- Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email ------- You are receiving this mail because: -------
Apr 12 2013
prev sibling next sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924


bearophile_hugs eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
         Resolution|FIXED                       |WONTFIX


-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 12 2013
prev sibling parent d-bugmail puremagic.com writes:
http://d.puremagic.com/issues/show_bug.cgi?id=9924


bearophile_hugs eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
         Resolution|WONTFIX                     |INVALID


-- 
Configure issuemail: http://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
Apr 12 2013