I'm new to Haskell & trying to generalise some behaviour in a program so as to reduce the amount of code and have a simpler solution overall.
Say I have this type which represents a post code e.g. 'ABC 123':
type PostCode = ((Char, Char, Char), (Int, Int, Int))
and the behaviour of a PostCode should be cylic; i.e. AAA 000 comes before AAA 001; 'A' to 'Z' for Char should rap-round, as should 0-9 for Int.
I've therefore extraced this type class:
class Cyclic a where
next :: a -> a
which is simple enough.
So I then have an instance for each:
-- import Data.Char
instance Cyclic Char where
next c = chr n
where nxt = (ord c) + 1
min = 65
max = 90
n = if (nxt > max) then min else nxt
instance Cyclic Int where
next i = n
where nxt = i + 1
min = 0
max = 9
n = if (nxt > max) then min else nxt
which leaves me with several obvious points:
the concept of min and max is duplicated
next is reimplemented for Int when the only real difference is the values of its min and max, as well as the type
How can I abstract this behaviour correctly? Is there a way to have the cyclic behaviour for both Int and Char without having two differing implementations? What other Haskell features might I use instead?
You can provide a default implementation for next in the type-class as in:
import Data.Char (chr)
class (Ord a, Enum a) => Cyclic a where
min', max' :: a
next :: a -> a
next a = if max' < a' then min' else a'
where a' = succ a
instance Cyclic Char where
min' = chr 65
max' = chr 90
instance Cyclic Int where
min' = 0
max' = 9
Alternatively you can use a helper function which implements next within a closure as in:
class Cyclic a where
next :: a -> a
-- a helper function to implement next within a closure
cycl :: (Ord a, Enum a) => a -> a -> (a -> a)
cycl min' max' a = if max' < a' then min' else a'
where a' = succ a
instance Cyclic Char where
next = cycl (chr 65) (chr 90)
instance Cyclic Int where
next = cycl 0 9
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