Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating functions over Enumerations

I just started learning Haskell. I think I've got the basics down, but I want to make sure I'm actually forcing myself to think functionally too.

data Dir = Right | Left | Front | Back | Up | Down deriving (Show, Eq, Enum)
inv Right = Left
inv Front = Back
inv Up = Down

Anyway, the jist of what I'm trying to do is to create a function to map between each "Dir" and its opposite/inv. I know I could easily continue this for another 3 lines, but I can't help but wonder if there's a better way. I tried adding:

inv a = b where inv b = a

but apparently you can't do that. So my question is: Is there either a way to generate the rest of the inverses or an altogether better way to create this function?

Thanks much.

like image 534
rcbuchanan Avatar asked Jun 06 '11 01:06

rcbuchanan


People also ask

Can enumerations have constructors?

Note: The constructor for an enum type must be package-private or private access. It automatically creates the constants that are defined at the beginning of the enum body. You cannot invoke an enum constructor yourself.

Is it better to use enum or constant?

Enums are lists of constants. When you need a predefined list of values which do represent some kind of numeric or textual data, you should use an enum. You should always use enums when a variable (especially a method parameter) can only take one out of a small set of possible values.

What are some of the advantages of creating an enumeration type?

The benefits of using enumerations include: Reduces errors caused by transposing or mistyping numbers. Makes it easy to change values in the future. Makes code easier to read, which means it is less likely that errors will creep into it.

What is the point of enumerations in Java?

Enumerations serve the purpose of representing a group of named constants in a programming language.


4 Answers

If the pairing between Up and Down and so on is an important feature then maybe this knowledge should be reflected in the type.

data Axis = UpDown | LeftRight | FrontBack
data Sign = Positive | Negative
data Dir = Dir Axis Sign

inv is now easy.

like image 180
sigfpe Avatar answered Oct 29 '22 03:10

sigfpe


Do you have a closed-form solution over the indices that corresponds to this function? If so, yes, you can use the Enum deriving to simplify things. For example,

import Prelude hiding (Either(..))

data Dir = Right
         | Front
         | Up

         | Left
         | Back
         | Down
     deriving (Show, Eq, Ord, Enum)

inv :: Dir -> Dir
inv x = toEnum ((3 + fromEnum x) `mod` 6)

Note, this relies on the ordering of the constructors!

*Main> inv Left
Right
*Main> inv Right
Left
*Main> inv Back
Front
*Main> inv Up
Down

This is very C-like, exploits the ordering of constructors, and is un-Haskelly. A compromise is to use more types, to define a mapping between the constructors and their mirrors, avoiding the use of arithmetic.

import Prelude hiding (Either(..))

data Dir = A NormalDir
         | B MirrorDir
     deriving Show

data NormalDir = Right | Front | Up
     deriving (Show, Eq, Ord, Enum)

data MirrorDir = Left  | Back  | Down     
     deriving (Show, Eq, Ord, Enum)

inv :: Dir -> Dir
inv (A n) = B (toEnum (fromEnum n))
inv (B n) = A (toEnum (fromEnum n))

E.g.

*Main> inv (A Right)
B Left
*Main> inv (B Down)
A Up

So at least we didn't have to do arithmetic. And the types distinguish the mirror cases. However, this is very un-Haskelly. It is absolute fine to enumerate the cases! Others will have to read your code at some point...

like image 29
Don Stewart Avatar answered Oct 29 '22 02:10

Don Stewart


pairs = ps ++ map swap ps where
   ps = [(Right, Left), (Front, Back), (Up, Down)]
   swap (a, b) = (b, a)

inv a = fromJust $ lookup a pairs    

[Edit]

Or how about this?

inv a = head $ delete a $ head $ dropWhile (a `notElem`)
        [[Right,Left],[Front,Back],[Up,Down]]
like image 41
Landei Avatar answered Oct 29 '22 02:10

Landei


It is good to know, that Enumeration starts with zero.

Mnemonic: fmap fromEnum [False,True] == [0,1]


import Data.Bits(xor)

-- Enum:       0   1          2   3       4   5
data Dir = Right | Left | Front | Back | Up | Down
           deriving (Read,Show,Eq,Ord,Enum,Bounded)

inv :: Dir -> Dir
inv = toEnum . xor 1 . fromEnum
like image 28
comonad Avatar answered Oct 29 '22 02:10

comonad