Custom Enum instance with non-consecutive indices

All, I've been working on a server implementation of an existing networking protocol. The protocol uses "magic constants" in some places (e.g. to tag message types), as well as bitfields, or a combination of both packed in a single value. I created data types for both the identifiers as well as the bitfield masks, e.g.
import Data.Bits
data MessageType = Message1 | Message2 | Message5
data MessageFlag = Flag1 | Flag2
Since I need to be able to get a numeric representation of them, I thought making a custom Enum instance would make sense:
instance Enum MessageType where fromEnum a = case a of Message1 -> 1 Message2 -> 2 Message5 -> 5 toEnum n | n == 1 = Message1 | n == 2 = Message2 | n == 5 = Message5
instance Enum MessageFlag where fromEnum a = case a of Flag1 -> 1 `shiftL` 0 Flag2 -> 1 `shiftL` 1 toEnum n | n == 1 `shiftL` 0 = Flag1 | n == 1 `shiftL` 1 = Flag2
This is not a complete definition (not to mention non-exhaustive pattern matches), so I was wondering what the best practices are to extend this so these instances are 'correct'? I've been trying several approaches, but all of them failed on code like
getFlags :: Int -> [MessageFlag] getFlags i = filter (\v -> (i .&. fromEnum v) /= 0) [Flag1 ..]
Unless I hard-code all options in the last list, of course, but this is obviously not the intention. Any help or pointers (including "This is not how Enum is intended to be used") would be appreciated! Thanks, Nicolas

Hi Nicolas,
The simplest approach would be to use the standard (derived) Enum
instance that would be used for enumerations (like [Flag1..]), and
have your own functions to convert to/from magic constants.
Roman
* Nicolas Trangez
All,
I've been working on a server implementation of an existing networking protocol. The protocol uses "magic constants" in some places (e.g. to tag message types), as well as bitfields, or a combination of both packed in a single value.
I created data types for both the identifiers as well as the bitfield masks, e.g.
import Data.Bits
data MessageType = Message1 | Message2 | Message5
data MessageFlag = Flag1 | Flag2
Since I need to be able to get a numeric representation of them, I thought making a custom Enum instance would make sense:
instance Enum MessageType where fromEnum a = case a of Message1 -> 1 Message2 -> 2 Message5 -> 5 toEnum n | n == 1 = Message1 | n == 2 = Message2 | n == 5 = Message5
instance Enum MessageFlag where fromEnum a = case a of Flag1 -> 1 `shiftL` 0 Flag2 -> 1 `shiftL` 1 toEnum n | n == 1 `shiftL` 0 = Flag1 | n == 1 `shiftL` 1 = Flag2
This is not a complete definition (not to mention non-exhaustive pattern matches), so I was wondering what the best practices are to extend this so these instances are 'correct'?
I've been trying several approaches, but all of them failed on code like
getFlags :: Int -> [MessageFlag] getFlags i = filter (\v -> (i .&. fromEnum v) /= 0) [Flag1 ..]
Unless I hard-code all options in the last list, of course, but this is obviously not the intention.
Any help or pointers (including "This is not how Enum is intended to be used") would be appreciated!

On Sat, 2012-11-17 at 16:52 +0200, Roman Cheplyaka wrote:
Hi Nicolas,
The simplest approach would be to use the standard (derived) Enum instance that would be used for enumerations (like [Flag1..]), and have your own functions to convert to/from magic constants.
Sure, but that kind-of defeats the purpose... fromEnum/toEnum would then return some nonsensical integer value. Nicolas

Nicolas Trangez
On Sat, 2012-11-17 at 16:52 +0200, Roman Cheplyaka wrote:
Hi Nicolas,
The simplest approach would be to use the standard (derived) Enum instance that would be used for enumerations (like [Flag1..]), and have your own functions to convert to/from magic constants.
Sure, but that kind-of defeats the purpose... fromEnum/toEnum would then return some nonsensical integer value.
...then don't derive an 'Enum' instance for that :-) what do you hope to gain from making your flags type an instance of the Enum class?

On Sat, 2012-11-17 at 16:27 +0100, Herbert Valerio Riedel wrote:
what do you hope to gain from making your flags type an instance of the Enum class?
Among others, the ability to list all flags using "[Flag1 ..]" How is this handled for libraries wrapping C libs which use some bitset structure? Nicolas

* Nicolas Trangez
On Sat, 2012-11-17 at 16:52 +0200, Roman Cheplyaka wrote:
Hi Nicolas,
The simplest approach would be to use the standard (derived) Enum instance that would be used for enumerations (like [Flag1..]), and have your own functions to convert to/from magic constants.
Sure, but that kind-of defeats the purpose... fromEnum/toEnum would then return some nonsensical integer value.
What's your purpose, exactly? I see two of them — being able to use enumerations, and converting from/to magical constants. If you want to use toEnum/fromEnum for conversions so badly, you can leave them as you defined them, but redefine the enumFrom* methods from the Enum class. But I'd still advise that you do your conversion outside of the Enum class, since such instance would be very unintuitive to someone who reads or uses your code. BTW, the value of the derived fromEnum is not nonsensical — it determines where in the [Flag1..] list a particular flag would appear. Roman
participants (3)
-
Herbert Valerio Riedel
-
Nicolas Trangez
-
Roman Cheplyaka