{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE IncoherentInstances #-}
module Control.Interchangeable
  (
    Interchangeable (..)
  )
where

import qualified Data.Set
import qualified Data.Map

-- |
-- Minimal definition is ('interchange', 'interchange'') and ('applyI',
-- 'applyI'') or ('modifyI', 'modifyI'').
class Interchangeable a b where
  -- | Interchange a with b
  interchange :: a -> b
  -- | Interchange b with a
  interchange' :: b -> a
  -- | Append a to b
  applyI :: a -> b -> b
  applyI a b = modifyI' b (const a)
  -- | Append b to a
  applyI' :: b -> a -> a
  applyI' b a = modifyI a (const b)
  -- | Modify a with an operation to b
  modifyI :: a -> (b -> b) -> a
  modifyI a f = applyI' (f (interchange a)) a
  -- | Modify a with an operation to b
  modifyI' :: b -> (a -> a) -> b
  modifyI' b f = applyI (f (interchange' b)) b

instance (Interchangeable a b) => Interchangeable b a where
  interchange = interchange'
  interchange' = interchange
  applyI = applyI'
  applyI' = applyI
  
instance (Ord a) => Interchangeable [a] (Data.Set.Set a) where
  interchange = Data.Set.fromList
  interchange' = Data.Set.toList
  applyI a = Data.Set.union (Data.Set.fromList a)
  applyI' b a = a ++ Data.Set.toList b

instance (Ord k) => Interchangeable [(k,a)] (Data.Map.Map k a) where
  interchange = Data.Map.fromList
  interchange' = Data.Map.toList
  applyI a = Data.Map.union (Data.Map.fromList a)
  applyI' b a = a ++ Data.Map.toList b