module Graphics.XHB.Monad
( XContext(..)
, X(..)
, unX
, toX
, MonadX(..)
, asksX
, notify
, reqAsync
, req
, pollEvent
, waitEvent
, WithReply(..)
, withReply
, (<$-)
, (<*-)
, doX
) where
import Graphics.XHB
import Graphics.XHB.Requests
import Data.Function
import Data.Typeable
import Control.Applicative
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Control.Monad.Trans.Identity as Identity (IdentityT, liftCatch)
import Control.Monad.Trans.List as List (ListT, liftCatch)
import Control.Monad.Trans.Maybe as Maybe (MaybeT, liftCatch)
import Control.Monad.Trans.Reader as Reader (ReaderT, liftCatch)
import Control.Monad.Trans.State.Lazy as LazyState (StateT, liftCatch)
import Control.Monad.Trans.State.Strict as StrictState (StateT, liftCatch)
import Control.Monad.Trans.Writer.Lazy as LazyWriter (WriterT, liftCatch)
import Control.Monad.Trans.Writer.Strict as StrictWriter (WriterT, liftCatch)
import Control.Monad.Except
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.State
class Monad x => XContext x where
request :: Request a => a -> Connection -> x ()
requestWithReply :: RequestWithReply a b => a -> Connection -> x (x (Either SomeError b))
waitXEvent :: Connection -> x SomeEvent
pollXEvent :: Connection -> x (Maybe SomeEvent)
instance XContext IO where
request = requestIO
requestWithReply = requestWithReplyIO
waitXEvent = waitForEvent
pollXEvent = pollForEvent
newtype WithReply x a = WithReply { runWithReply :: Connection -> x (x (Either SomeError a)) }
instance Functor x => Functor (WithReply x) where
fmap f = WithReply . (fmap . fmap . fmap . fmap) f . runWithReply
instance Monad x => Applicative (WithReply x) where
pure = WithReply . pure . pure . pure . pure
f <*> a = WithReply $ \conn -> do
xf <- runWithReply f conn
xa <- runWithReply a conn
return . runExceptT $ ExceptT xf <*> ExceptT xa
withReply :: (XContext x, RequestWithReply a b) => a -> WithReply x b
withReply = WithReply . requestWithReply
infixl 5 <$-, <*-
(<$-) :: (XContext x, RequestWithReply a b) => (b -> c) -> a -> WithReply x c
f <$- a = f <$> withReply a
(<*-) :: (XContext x, RequestWithReply a b) => WithReply x (b -> c) -> a -> WithReply x c
f <*- a = f <*> withReply a
doX :: MonadX x m => WithReply x a -> m a
doX x = do
conn <- askX
liftX (join (runWithReply x conn)) >>= either throwErrorX return
newtype X m a = X { runX :: ReaderT Connection (ExceptT SomeError m) a }
deriving (Functor, Applicative, Monad, MonadIO, Typeable)
instance MonadTrans X where
lift = X . lift . lift
toX :: (Connection -> m (Either SomeError a)) -> X m a
toX = X . ReaderT . fmap ExceptT
unX :: X m a -> Connection -> m (Either SomeError a)
unX = fmap runExceptT . runReaderT . runX
class (XContext x, Monad m) => MonadX x m | m -> x where
liftX :: x a -> m a
askX :: m Connection
catchErrorX :: m a -> (SomeError -> m a) -> m a
throwErrorX :: SomeError -> m a
instance XContext x => MonadX x (X x) where
liftX = X . lift . lift
askX = X ask
catchErrorX m f = X $ catchError (runX m) (runX . f)
throwErrorX = X . throwError
asksX :: MonadX x m => (Connection -> a) -> m a
asksX = flip fmap askX
notify :: (MonadX x m, Request a) => a -> m ()
notify a = askX >>= (liftX . request a)
reqAsync :: (MonadX x m, RequestWithReply a b) => a -> m (m b)
reqAsync a = do
conn <- askX
x <- liftX $ requestWithReply a conn
either throwErrorX return <$> liftX x
req :: (MonadX x m, RequestWithReply a b) => a -> m b
req a = do
conn <- askX
liftX (join (requestWithReply a conn)) >>= either throwErrorX return
waitEvent :: MonadX x m => m SomeEvent
waitEvent = askX >>= liftX . waitXEvent
pollEvent :: MonadX x m => m (Maybe SomeEvent)
pollEvent = askX >>= liftX . pollXEvent
instance MonadError e m => MonadError e (X m) where
throwError = lift . throwError
catchError x f = toX $ \conn -> catchError (unX x conn) (flip ($) conn . unX . f)
instance MonadReader r m => MonadReader r (X m) where
ask = lift ask
local f x = toX $ \conn -> local f (unX x conn)
instance MonadState s m => MonadState s (X m) where
get = lift get
put = lift . put
instance MonadWriter w m => MonadWriter w (X m) where
tell = lift . tell
listen = X . listen . runX
pass = X . pass . runX
instance MonadX x m => MonadX x (ExceptT e m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX m f = ExceptT $ catchErrorX (runExceptT m) (runExceptT . f)
instance MonadX x m => MonadX x (IdentityT m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = Identity.liftCatch catchErrorX
instance MonadX x m => MonadX x (ListT m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = List.liftCatch catchErrorX
instance MonadX x m => MonadX x (MaybeT m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = Maybe.liftCatch catchErrorX
instance MonadX x m => MonadX x (ReaderT r m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = Reader.liftCatch catchErrorX
instance MonadX x m => MonadX x (LazyState.StateT s m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = LazyState.liftCatch catchErrorX
instance MonadX x m => MonadX x (StrictState.StateT s m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = StrictState.liftCatch catchErrorX
instance (Monoid w, MonadX x m) => MonadX x (LazyWriter.WriterT w m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = LazyWriter.liftCatch catchErrorX
instance (Monoid w, MonadX x m) => MonadX x (StrictWriter.WriterT w m) where
liftX = lift . liftX
askX = lift askX
throwErrorX = lift . throwErrorX
catchErrorX = StrictWriter.liftCatch catchErrorX