packages feed

ideas-0.6: src/Common/Traversable.hs

-----------------------------------------------------------------------------
-- Copyright 2010, Open Universiteit Nederland. This file is distributed 
-- under the terms of the GNU General Public License. For more information, 
-- see the file "LICENSE.txt", which is included in the distribution.
-----------------------------------------------------------------------------
-- |
-- Maintainer  :  bastiaan.heeren@ou.nl
-- Stability   :  provisional
-- Portability :  portable (depends on ghc)
--
-----------------------------------------------------------------------------
module Common.Traversable 
   ( Once(..), Switch(..), Crush(..), OnceJoin(..), useOnceJoin
   ) where

import Control.Monad.Identity
import qualified Data.IntMap as IM
import qualified Data.Map as M

{- Examples:

once (^2) [1..3]
   ~>  [[1,2,3],[1,4,3],[1,2,9]]

onceM (\x -> [x+1, x^2]) [1..3]
   ~>  [[2,2,3],[1,2,3],[1,3,3],[1,4,3],[1,2,4],[1,2,9]]

onceJoin (\x -> [x+1, x^2]) [1..3]
   ~>  [[2,1,2,3],[1,3,4,3],[1,2,4,9]]

onceJoinM (\x -> [[x+1], [x^2, x^3]]) [1..3]
   ~>  [[2,2,3],[1,1,2,3],[1,3,3],[1,4,8,3],[1,2,4],[1,2,9,27]]
-}

-----------------------------------------------------------
-- * Type class |Once|

class Functor f => Once f where
   -- | Apply a function once in a given structure
   once :: (a -> a) -> f a -> [f a]
   -- | Apply a monadic function once in a given structure
   onceM :: MonadPlus m => (a -> m a) -> f a -> m (f a)
   
   -- default definition
   once f = onceM (return . f)

instance Once [] where
   onceM = useOnceJoin
   
instance Once Maybe where
   onceM = useOnceJoin
   
instance Once Identity where
   onceM = useOnceJoin

instance Eq a => Once (M.Map a) where
   onceM f m = liftM M.fromAscList (onceM g (M.toList m))
    where g (a, b) = liftM (\c -> (a, c)) (f b)

instance Once IM.IntMap where
   onceM f m = liftM IM.fromAscList (onceM g (IM.toList m))
    where g (a, b) = liftM (\c -> (a, c)) (f b)

useOnceJoin :: (OnceJoin f, MonadPlus m) => (a -> m a) -> f a -> m (f a)
useOnceJoin f = onceJoinM (liftM return . f)

-----------------------------------------------------------
-- * Type class |Switch|

class Functor f => Switch f where
   switch :: Monad m => f (m a) -> m (f a)
         
instance Switch [] where
   switch = sequence

instance Switch Maybe where
   switch = maybe (return Nothing) (liftM Just)

instance Switch Identity where
   switch (Identity m) = liftM Identity m

instance Eq a => Switch (M.Map a) where
   switch m = do
      let (ns, ms) = unzip (M.toList m)
      as <- sequence ms 
      return $ M.fromAscList $ zip ns as

instance Switch IM.IntMap where
   switch m = do
      let (ns, ms) = unzip (IM.toList m)
      as <- sequence ms 
      return $ IM.fromAscList $ zip ns as

-----------------------------------------------------------
-- * Type class |Crush|

class Functor f => Crush f where
   crush :: f a -> [a]

instance Crush [] where
   crush = id

instance Crush Maybe where
   crush = maybe [] return

instance Crush Identity where
   crush = return . runIdentity

instance Crush (M.Map a) where
   crush = M.elems

instance Crush IM.IntMap where
   crush = IM.elems

-----------------------------------------------------------
-- * Type class |OnceJoin|

class (Once f, Monad f) => OnceJoin f where
   -- | Apply a function once in a given structure, join the result afterwards
   onceJoin :: (a -> f a) -> f a -> [f a]
   -- | Apply a monadic function once in a given structure, join the result afterwards
   onceJoinM :: MonadPlus m => (a -> m (f a)) -> f a -> m (f a)

   -- default definition
   onceJoin f = onceJoinM (return . f)

instance OnceJoin [] where
   onceJoinM _ []     = mzero 
   onceJoinM f (x:xs) = liftM (++xs) (f x) `mplus` liftM (x:) (onceJoinM f xs)

instance OnceJoin Maybe where
   onceJoinM = maybe mzero
   
instance OnceJoin Identity where
   onceJoinM f = f . runIdentity