hans-3.0.2: src/Hans/Monad.hs
{-# LANGUAGE MultiParamTypeClasses #-}
module Hans.Monad (
Hans()
, runHans, runHansOnce
, setEscape, escape, dropPacket
, callCC
, io
, decode, decode'
) where
import Hans.Device.Types (DeviceStats(),updateDropped,statRX)
import qualified Control.Applicative as A
import qualified Data.ByteString as S
import Data.IORef (newIORef,writeIORef,readIORef)
import Data.Serialize.Get (runGet,runGetState,Get)
import MonadLib (BaseM(..))
newtype Hans a = Hans { unHans :: IO () -> (a -> IO ()) -> IO () }
instance Functor Hans where
fmap f m = Hans (\ e k -> unHans m e (k . f))
{-# INLINE fmap #-}
instance A.Applicative Hans where
pure x = Hans (\ _ k -> k x)
f <*> x = Hans $ \ e k -> unHans f e
$ \ g -> unHans x e
$ \ y -> k (g y)
{-# INLINE pure #-}
{-# INLINE (<*>) #-}
instance Monad Hans where
return = A.pure
m >>= f = Hans $ \ e k -> unHans m e
$ \ a -> unHans (f a) e k
{-# INLINE return #-}
{-# INLINE (>>=) #-}
instance BaseM Hans IO where
inBase = io
{-# INLINE inBase #-}
-- | Run only one iteration of the Hans monad.
runHansOnce :: Hans a -> IO (Maybe a)
runHansOnce (Hans f) =
do res <- newIORef Nothing
f (writeIORef res Nothing) (\x -> writeIORef res (Just x))
readIORef res
-- | Loop forever, running the underlying operation.
runHans :: Hans () -> IO ()
runHans (Hans m) = loop ()
where
loop () = m (loop ()) loop
{-# INLINE runHans #-}
-- | Set the escape continuation to the current position, within the operation
-- given, so that control flow always restores back through this point.
setEscape :: Hans () -> Hans ()
setEscape m = Hans (\ _ k -> unHans m (k ()) k)
{-# INLINE setEscape #-}
-- | Invoke the escape continuation.
escape :: Hans a
escape = Hans (\ e _ -> e)
{-# INLINE escape #-}
-- | Call-cc. NOTE: the continuation will inherit the escape point of the
-- calling context.
callCC :: ((b -> Hans a) -> Hans b) -> Hans b
callCC f = Hans (\e k -> unHans (f (\b -> Hans (\_ _ -> k b))) e k)
{-# INLINE callCC #-}
-- | Synonym for 'escape' that also updates device statistics.
dropPacket :: DeviceStats -> Hans a
dropPacket stats =
do io (updateDropped statRX stats)
escape
{-# INLINE dropPacket #-}
-- | Lift an 'IO' operation into 'Hans'.
io :: IO a -> Hans a
io m = Hans (\ _ k -> m >>= k )
{-# INLINE io #-}
-- | Run a Get action in the context of the Hans monad,
decode :: DeviceStats -> Get a -> S.ByteString -> Hans a
decode dev m bytes =
case runGet m bytes of
Right a -> return a
Left _ -> dropPacket dev
{-# INLINE decode #-}
-- | Run a Get action in the context of the Hans monad, returning any unconsumed
-- input.
decode' :: DeviceStats -> Get a -> S.ByteString -> Hans (a,S.ByteString)
decode' dev m bytes =
case runGetState m bytes 0 of
Right a -> return a
Left _ -> dropPacket dev