{-# LANGUAGE GeneralizedNewtypeDeriving, StandaloneDeriving #-}

------------------------------------------------------------------------------------
-- |
-- Copyright   : (c) Hans Hoglund 2012
--
-- License     : BSD-style
--
-- Maintainer  : hans@hanshoglund.se
-- Stability   : experimental
-- Portability : non-portable (TF,GNTD)
--
-------------------------------------------------------------------------------------

module Music.Pitch.Alterable (
        -- * Alterable class
        Alterable(..),
        alter,
  ) where

import Data.Ratio

-- |
-- Class of things that can be altered.
--
-- > accidental (sharpen a) = sharpen (accidental a)
-- > accidental (flatten a) = flatten (accidental a)
-- > sharpen . flatten      = id
--
class Alterable a where
    -- | 
    -- Increase the given pitch by one.
    -- 
    sharpen :: a -> a

    -- | 
    -- Decrease the given pitch by one.
    -- 
    flatten :: a -> a


instance Alterable a => Alterable (b -> a) where
    sharpen = fmap sharpen
    flatten = fmap flatten

instance Alterable Double where
    sharpen = (+ 1)
    flatten = (subtract 1)

instance Alterable Integer where
    sharpen = (+ 1)
    flatten = (subtract 1)

instance Integral a => Alterable (Ratio a) where
    sharpen = (+ 1)
    flatten = (subtract 1)

instance Alterable a => Alterable [a] where
    sharpen = fmap sharpen
    flatten = fmap flatten

instance Alterable a => Alterable (b, a) where
    sharpen = fmap sharpen
    flatten = fmap flatten

{-
sharpened :: Alterable a => Iso' a a
sharpened = iso sharpen flatten

flattened :: Alterable a => Iso' a a
flattened = iso flatten sharpen
-}

alter :: Alterable a => Int -> a -> a
alter n x
  | n < 0  = iterate flatten x !! n
  | n == 0 = x
  | n > 0  = iterate sharpen x !! n