safe-freeze-0.2: Control/Monad/ST/Freeze.hs
-----------------------------------------------------------------------------
-- |
-- Module: Control.Monad.ST.Freeze
-- License: BSD3
-- Maintainer: Reiner Pope <reiner.pope@gmail.com>
-- Stability: experimental
-- Portability: portable
--
-- An indexed version of the ST monad with support for safely freezing
-- multiple arrays. Compare to the usual ST monad, where the only support
-- for safely freezing arrays is runSTUArray -- only one array may be frozen.
--
-- This version of the ST monad has two distinct stages of processing:
-- the /normal/ stage, and the /freeze/ stage. Reading and writing are
-- permitted in the normal stage; reading and freezing are permitted
-- in the freeze stage. This policy ensures that no writes occur after
-- the arrays have been frozen.
--
-- This ST is an /indexed/ monad (see "Control.Monad.Indexed") as well
-- as a normal monad. That is, each monadic value will have an
-- \"ingoing\" state thread as well as an \"outgoing\" state
-- thread. These state threads are similar to the ST monad's state
-- thread, except that they are now annotated with a stage name:
-- either 'Normal' or 'Freeze'.
-----------------------------------------------------------------------------
module Control.Monad.ST.Freeze (
-- * Stage names
Normal,
Freeze,
-- * MonadST class
MonadST(..),
STNormal,
STFreeze,
STRead,
-- * ST monad
ST,
runST,
-- * ST transformer
STT,
STTBase(..),
) where
import Control.Applicative
import Control.Monad
import Control.Monad.Identity(Identity(..))
import qualified Control.Monad.ST as S (ST)
import qualified Control.Monad.ST.Unsafe as S
import Control.Monad.Indexed(IxMonad(..),IxFunctor(..),IxPointed(..),IxApplicative(..))
import System.IO.Unsafe
data Normal s
data Freeze s
-- | A computation containing some writes but no freezes: it starts
-- | and ends in the 'Normal' stage.
type STNormal st s a = st (Normal s) (Normal s) a
-- | A computation containing only reads: it starts and ends in any
-- stage, but does not change stage. (Note that there would be no loss
-- of safety in allowing the stage to change, but it may result in
-- ambiguous types, or extra type annotations being required.)
type STRead st s a = forall stg. st (stg s) (stg s) a
-- | A computation containing some freezes but no writes: it starts in
-- | any stage, but ends in the Freeze stage.
type STFreeze st s a = forall stg. st (stg s) (Freeze s) a
class IxMonad st => MonadST st where
-- | For lifting any operations containing writes but no freezes.
liftST :: S.ST s a -> STNormal st s a
-- | For lifting any operations containing reads but no writes or freezes.
liftRead :: S.ST s a -> STRead st s a
-- | For lifting an @unsafeFreeze@ operation
liftUnsafeFreeze :: S.ST s a -> STFreeze st s a
-- | An ST monad transformer. However, this is not a genuine monad
-- transformer, as that would be unsafe (see
-- <http://www.haskell.org/pipermail/glasgow-haskell-users/2009-February/016554.html>). To
-- retain safety, it may only act as a monad transformer over the
-- 'Identity' and 'IO' monads, enforced by the 'STTBase' typeclass.
--
-- Defining 'STT' as a monad transformer rather than just a monad
-- allows ST arrays to be used in the 'IO' monad, bringing safe
-- freezing also to the 'IO' monad.
newtype STT (m :: * -> *) s t a = STT { unSTT :: IO a }
class STTBase m where
-- | Runs the monad
runSTT :: (forall s. STT m (stg1 s) (stg2 s) a) -> m a
-- | A restricted analog of Control.Monad.Trans.lift.
lift :: m a -> STT m (stg s) (stg s) a
type ST = STT Identity
-- | @runST = 'runIdentity' . 'runSTT'@
runST :: (forall s. STT Identity (stg1 s) (stg2 s) a) -> a
runST = runIdentity . runSTT
instance STTBase IO where
runSTT m = unSTT m
lift = STT
instance STTBase Identity where
runSTT m = Identity (unsafePerformIO (unSTT m))
lift = STT . return . runIdentity
instance IxFunctor (STT m) where
imap f (STT m) = STT (fmap f m)
instance IxPointed (STT m) where
ireturn a = STT (return a)
instance IxApplicative (STT m) where
iap (STT m) (STT n) = STT (m `ap` n)
instance IxMonad (STT m) where
ibind k (STT m) = STT (m >>= (unSTT . k))
instance MonadST (STT m) where
liftST s = STT (S.unsafeSTToIO s)
liftRead s = STT (S.unsafeSTToIO s)
liftUnsafeFreeze s = STT (S.unsafeSTToIO s)
instance Functor (STT m s t) where
fmap f = imap f
instance Applicative (STT m s s) where
pure = ireturn
(<*>) = iap
instance Monad (STT m s s) where
(>>=) = flip ibind
return = ireturn