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.
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.
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.
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.
Enumerations serve the purpose of representing a group of named constants in a programming language.
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.
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...
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]]
It is good to know, that Enum
eration 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With