packages feed

phooey-0.1: src/Graphics/UI/Phooey/TagT.hs

{-# OPTIONS -fglasgow-exts -fallow-undecidable-instances #-}

----------------------------------------------------------------------
-- |
-- Module      :  Graphics.UI.Phooey.TagT
-- Copyright   :  (c) Conal Elliott 2006
-- License     :  LGPL
-- 
-- Maintainer  :  conal@conal.net
-- Stability   :  provisional
-- Portability :  portable
-- 
-- A /tagging/ monad transformer
----------------------------------------------------------------------

module Graphics.UI.Phooey.TagT
  (
  -- * Convenient function synonyms
    Unop, Binop
  -- * The /tagging/ monad transformer (or tagged-value maker)
  , TagT(..), runTag
  -- * Transforming tagged-value makers
  , TagTOp, mapTag, mkTag
  -- * Tag-combiner tweakers
  , updateTagComb, setTagComb, flipTagComb
  ) where

-- import Control.Monad.Fix
import Control.Monad.Reader
import Control.Monad.Trans
import Control.Arrow (first)

{----------------------------------------------------------
    Convenient function synonyms
----------------------------------------------------------}

type Unop   a = a -> a
type Binop  a = a -> a -> a


{----------------------------------------------------------
    The /tagging/ monad transformer / tagged-value maker.
    Tags may be present or absent and are combined according to
    a contextual 'Binop'.
----------------------------------------------------------}

newtype TagT tag m a =
  T { unT :: ReaderT (Binop tag) m (Maybe tag,a) }

-- | Run a 'TagT'
runTag :: Binop tag       -- ^ the tag combiner
       -> TagT tag m a
       -> m (Maybe tag,a)
runTag op (T r) = runReaderT r op

instance Monad m => Monad (TagT tag m) where
  return a   = lift (return a)
  wta >>= f  = T (do  (ta,a)  <- unT wta
                      (tb,b)  <- unT (f a)
                      op      <- ask
                      return (mb op ta tb, b))
    where
      mb op (Just a)  (Just b)  = Just (a `op` b)
      mb _  mba       mbb       = mba `mplus` mbb

instance MonadFix m => MonadFix (TagT tag m) where
  mfix f = T (mfix (\ ~(_,a) ->  unT (f a)))


{----------------------------------------------------------
    Transforming tagged-value makers
----------------------------------------------------------}

-- | Transforming tagged-value makers into others
type TagTOp tag m = forall a. Unop (TagT tag m a)

-- | Tag changer
mapTag :: Monad m => Unop tag -> TagTOp tag m
mapTag f (T reader) = T (liftM (first (fmap f)) reader)

-- | Make a tagged-value maker from a tag and a value
mkTag :: Monad m => m (Maybe tag,a) -> TagT tag m a
mkTag m = T (lift m)

{----------------------------------------------------------
    Tag-combiner tweakers
----------------------------------------------------------}

-- | Temporarily /update/ the tag combiner
updateTagComb  ::  Monad m =>
                 Unop (Binop tag)
             ->  Unop (TagT tag m a)
updateTagComb h (T r) = T (local h r)

-- Augment the context for the duration of a computation.
-- Maybe rename and put back in MonadLib
-- updateR :: RunReaderM m i => (i->i) -> m a -> m a
-- updateR f m = do r <- ask
--                  local (f r) m


-- | Temporarily /replace/ the tag combiner
setTagComb  ::  Monad m => Binop tag ->  Unop (TagT tag m a)
setTagComb op = updateTagComb (const op)

-- | Temporarily /reverse/ the tag combiner
flipTagComb ::  Monad m => Unop (TagT tag m a)
flipTagComb = updateTagComb flip

instance MonadTrans (TagT tag) where
  lift m = T (lift (do  a <- m
                        return (Nothing,a)))

-- instance BaseM m b => BaseM (TagT tag m) b where
--   inBase m = lift (inBase m)

-- Add more transformer instances