Here is a tiny example that I’ve been playing around with the other day:
data Note = C | D | E | F | G | A | B deriving (Show, Enum, Bounded, Eq, Read)
buildTuple :: [a] -> [(Maybe a, Maybe a)]
buildTuple xs = zip z $ Nothing:z
where
z = map Just xs
getPreviousElement :: [Note] -> Maybe Note
getPreviousElement [] = Nothing
getPreviousElement xs = snd $ last $ buildTuple xs
addNote :: [Note] -> [Maybe Note]
addNote xs = map Just xs ++ [getPreviousElement xs >>= succNote]
succNote :: Note -> Maybe Note
succNote n
| n == maxBound = Just minBound
| otherwise = Just $ succ n
parseBrackets :: String -> String
parseBrackets ('{':xs) = '[' : parseBrackets xs
parseBrackets ('}':xs) = ']' : parseBrackets xs
parseBrackets (x:xs) = x : parseBrackets xs
parseBrackets x = x
main :: IO [Maybe Note]
main = getLine >>= \xs -> return (addNote (read (parseBrackets xs) :: [Note]))
This example demonstrates a couple of things:
1. How we can use the Haskell’s type system to define a list of Notes
2. The use of the zip function, to show how easy it is to get a previous element in a list (what buildTuple does is e.g. for [A,B,C] it would produce [(Just A, Nothing), (Just B, Just A), (Just C, Just B)], so that we have a track of previous elements)
3. It demonstrates reading and playing around with IO and parsing stuff on our own way