packages feed

halfs-2.0: Halfs/Monad.hs

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}

module Halfs.Monad
  (
    module Control.Monad.Reader
  , MonadError(..)
  , HalfsT
  , atomicModifyLockedRscRef
  , hbracket
  , newLockedRscRef
  , runHalfs
  , runHalfsNoEnv
  , withLock
  , withLockM
  , withLockedRscRef
  )
  where

import Control.Monad.Error
import Control.Monad.Reader

import Halfs.Classes
import Halfs.Types

newtype HalfsT err env m a = HalfsT { runHalfsT :: ReaderT env m (Either err a) }

runHalfs :: env -> HalfsT err (Maybe env) m a -> m (Either err a)
runHalfs = flip (runReaderT . runHalfsT) . Just

runHalfsNoEnv :: HalfsT err (Maybe env) m a -> m (Either err a)
runHalfsNoEnv = flip (runReaderT . runHalfsT) Nothing

--------------------------------------------------------------------------------
-- Instances

instance (Monad m) => Monad (HalfsT err env m) where
  m >>= k = HalfsT $ runHalfsT m >>= either (return . Left) (runHalfsT . k)
  return  = HalfsT . return . Right

instance (Monad m) => Functor (HalfsT err env m) where
  fmap f fa =
    HalfsT $ runHalfsT fa >>= either (return . Left) (return . Right . f)

instance MonadTrans (HalfsT err env) where
  lift = HalfsT . lift . liftM Right
    -- HalfsT . (=<<) (return . Right)

instance (Monad m) => MonadError err (HalfsT err env m) where
  throwError e = HalfsT $ return $ Left e
  catchError act h =
    HalfsT $ do
      eea <- runHalfsT act
      case eea of
        Left  e -> runHalfsT (h e)
        Right a -> return $ Right a

instance Monad m => MonadReader r (HalfsT err r m) where
  ask       = HalfsT $ ReaderT $ return . Right
  local f m = HalfsT $ ReaderT $ \r -> runReaderT (runHalfsT m) (f r)
  
instance Reffable r m => Reffable r (HalfsT err env m) where
  newRef     = lift . newRef     
  readRef    = lift . readRef    
  writeRef r = lift . writeRef r 

instance Bitmapped b m => Bitmapped b (HalfsT err env m) where
  newBitmap w = lift . newBitmap w 
  clearBit b  = lift . clearBit b
  setBit b    = lift . setBit b
  checkBit b  = lift . checkBit b
  toList      = lift . toList

instance Lockable l m => Lockable l (HalfsT err env m) where
  newLock = lift newLock
  lock    = lift . lock
  release = lift . release

instance Timed t m => Timed t (HalfsT err env m) where
  getTime   = lift getTime
  toCTime   = lift . toCTime
  fromCTime = lift . fromCTime

instance Threaded m => Threaded (HalfsT err env m) where
  getThreadId = lift getThreadId

--------------------------------------------------------------------------------
-- Utility functions specific to the Halfs monad

hbracket :: Monad m =>
            HalfsT err env m a         -- ^ before (\"acquisition\")
         -> (a -> HalfsT err env m b)  -- ^ after  (\"release\")
         -> (a -> HalfsT err env m c)  -- ^ bracketed computation
         -> HalfsT err env m c         -- ^ result of bracketed computation
hbracket before after act = do
  a <- before
  r <- act a `catchError` \e -> after a >> throwError e
  _ <- after a
  return r

withLock :: HalfsCapable b t r l m =>
            l -> HalfsT err env m a -> HalfsT err env m a
withLock l act = hbracket (lock l) (const $ release l) (const act)

withLockM :: (Monad m, Lockable l m) =>
             l -> m a -> m a
withLockM l act = do
  lock l
  res <- act
  release l
  return res
         
--------------------------------------------------------------------------------
-- Locked resource reference utility functions

newLockedRscRef :: (Lockable l m, Functor m, Reffable r m) =>
                   rsc
                -> m (LockedRscRef l r rsc)
newLockedRscRef initial = LockedRscRef `fmap` newLock `ap` newRef initial

withLockedRscRef :: HalfsCapable b t r l m =>
                    LockedRscRef l r rsc
                 -> (r rsc -> HalfsT err env m rslt)
                 -> HalfsT err env m rslt
withLockedRscRef lr f = withLock (lrLock lr) $ f (lrRsc lr)

atomicModifyLockedRscRef :: HalfsCapable b t r l m =>
                            LockedRscRef l r rsc
                         -> (rsc -> rsc)
                         -> HalfsT err env m ()
atomicModifyLockedRscRef lr f = withLockedRscRef lr (`modifyRef` f)