{-#
LANGUAGE
GADTs,
Rank2Types,
TupleSections,
KindSignatures,
ScopedTypeVariables,
QuantifiedConstraints
#-}
module Control.Monad.Script (
ScriptT
, ScriptTT()
, execScriptTT
, liftScriptTT
, except
, triage
, throw
, catch
, ask
, local
, transport
, reader
, tell
, draft
, listen
, pass
, censor
, get
, put
, modify
, modify'
, gets
, prompt
, checkScriptTT
) where
import Control.Monad
( ap, join )
import Control.Monad.Trans.Class
( MonadTrans(..) )
import Control.Monad.Trans.Identity
( IdentityT(..) )
import Data.Functor.Classes
()
import Data.Functor.Identity
( Identity(..) )
import Data.Monoid
()
import Data.Typeable
( Typeable )
import Test.QuickCheck
( Property, Gen, Arbitrary(..), CoArbitrary(..) )
import Test.QuickCheck.Monadic
( monadicIO, run, assert )
data
ScriptTT
(e :: *)
(r :: *)
(w :: *)
(s :: *)
(p :: * -> *)
(t :: (* -> *) -> * -> *)
(eff :: * -> *)
(a :: *)
where
ScriptTT
:: (Monad eff, Monad (t eff), MonadTrans t)
=> ((s,r)
-> forall v.
((Either e a, s, w) -> t eff v)
-> (forall u. p u -> (u -> t eff v) -> t eff v)
-> t eff v)
-> ScriptTT e r w s p t eff a
deriving Typeable
runScriptTT
:: ScriptTT e r w s p t eff a
-> (s,r)
-> forall v.
((Either e a, s, w) -> t eff v)
-> (forall u. p u -> (u -> t eff v) -> t eff v)
-> t eff v
runScriptTT (ScriptTT x) = x
instance
(Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> Monad (ScriptTT e r w s p t eff) where
return x = ScriptTT $ \(s,_) -> \end _ ->
end (Right x, s, mempty)
x >>= f = ScriptTT $ \(s0,r) -> \end cont -> do
let
g (z1,s1,w1) = case z1 of
Right y -> do
let h (z2,s2,w2) = end (z2, s2, mappend w1 w2)
runScriptTT (f y) (s1,r) h cont
Left e -> do
let h (_,s2,w2) = end (Left e, s2, mappend w1 w2)
runScriptTT (return ()) (s1,r) h cont
runScriptTT x (s0,r) g cont
instance
(Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> Applicative (ScriptTT e r w s p t eff) where
pure = return
(<*>) = ap
instance
(Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> Functor (ScriptTT e r w s p t eff) where
fmap f x = x >>= (return . f)
instance
(Monoid w, forall m. (Monad m) => Monad (t m), MonadTrans t)
=> MonadTrans (ScriptTT e r w s p t) where
lift x = ScriptTT $ \(s,_) -> \end _ ->
lift x >>= \a -> end (Right a, s, mempty)
liftScriptTT
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> t eff a -> ScriptTT e r w s p t eff a
liftScriptTT x = ScriptTT $ \(s,_) -> \end _ -> do
a <- x
end (Right a, s, mempty)
type ScriptT e r w s p = ScriptTT e r w s p IdentityT
execScriptTC
:: s
-> r
-> ((Either e a, s, w) -> t eff v)
-> (forall u. p u -> (u -> t eff v) -> t eff v)
-> ScriptTT e r w s p t eff a
-> t eff v
execScriptTC s r end cont (ScriptTT run) =
run (s,r) end cont
execScriptTT
:: (Monad eff, Monad (t eff), MonadTrans t)
=> s
-> r
-> (forall u. p u -> eff u)
-> ScriptTT e r w s p t eff a
-> t eff (Either e a, s, w)
execScriptTT s r eval =
execScriptTC s r return
(\p c -> (lift $ eval p) >>= c)
checkScriptTT
:: (Monad eff, Monad (t eff), MonadTrans t, Show q)
=> s
-> r
-> (forall u. p u -> eff u)
-> (t eff (Either e a, s, w) -> IO q)
-> (q -> Bool)
-> ScriptTT e r w s p t eff a
-> Property
checkScriptTT s r eval cond check script = monadicIO $ do
let result = execScriptTT s r eval script
q <- run $ cond result
assert $ check q
ask
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> ScriptTT e r w s p t eff r
ask = ScriptTT $ \(s,r) -> \end _ ->
end (Right r, s, mempty)
local
:: (Monad eff, Monad (t eff), MonadTrans t)
=> (r -> r)
-> ScriptTT e r w s p t eff a
-> ScriptTT e r w s p t eff a
local = transport
transport
:: (Monad eff, Monad (t eff), MonadTrans t)
=> (r2 -> r1)
-> ScriptTT e r1 w s p t eff a
-> ScriptTT e r2 w s p t eff a
transport f x = ScriptTT $ \(s,r) -> \end cont ->
runScriptTT x (s, f r) end cont
reader
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t, Monad (t eff))
=> (r -> a)
-> ScriptTT e r w s p t eff a
reader f = fmap f ask
get
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> ScriptTT e r w s p t eff s
get = ScriptTT $ \(s,_) -> \end _ ->
end (Right s, s, mempty)
put
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> s
-> ScriptTT e r w s p t eff ()
put s = ScriptTT $ \(_,_) -> \end _ ->
end (Right (), s, mempty)
modify
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> (s -> s)
-> ScriptTT e r w s p t eff ()
modify f = ScriptTT $ \(s,_) -> \end _ ->
end (Right (), f s, mempty)
modify'
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> (s -> s)
-> ScriptTT e r w s p t eff ()
modify' f = ScriptTT $ \(s,_) -> \end _ ->
end (Right (), f $! s, mempty)
gets
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> (s -> a)
-> ScriptTT e r w s p t eff a
gets f = ScriptTT $ \(s,_) -> \end _ ->
end (Right (f s), s, mempty)
tell
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> w
-> ScriptTT e r w s p t eff ()
tell w = ScriptTT $ \(s,_) -> \end _ ->
end (Right (), s, w)
draft
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> ScriptTT e r w s p t eff a
-> ScriptTT e r w s p t eff (a,w)
draft x = ScriptTT $ \(r,s) -> \end cont ->
runScriptTT x (r,s)
(\(y,s,w) -> end (fmap (,w) y, s, mempty)) cont
listen
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> ScriptTT e r w s p t eff a
-> ScriptTT e r w s p t eff (a,w)
listen x = ScriptTT $ \(r,s) -> \end cont ->
runScriptTT x (r,s)
(\(y,s,w) -> end (fmap (,w) y, s, w)) cont
pass
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> ScriptTT e r w s p t eff (a, w -> w)
-> ScriptTT e r w s p t eff a
pass x = ScriptTT $ \(r,s) -> \end cont ->
let
end' (z,s1,w) = case z of
Right (y,f) -> end (Right y, s1, f w)
Left e -> end (Left e, s1, w)
in
runScriptTT x (r,s) end' cont
censor
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> (w -> w)
-> ScriptTT e r w s p t eff a
-> ScriptTT e r w s p t eff a
censor f x = pass $ ScriptTT $ \(s,r) -> \end cont ->
let
end' (z,s1,w) = case z of
Right y -> end (Right (y,f), s1, w)
Left e -> end (Left e, s1, w)
in
runScriptTT x (s,r) end' cont
except
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> Either e a
-> ScriptTT e r w s p t eff a
except z = ScriptTT $ \(s,_) -> \end _ ->
end (z, s, mempty)
triage
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> (e1 -> e2)
-> ScriptTT e1 r w s p t eff a
-> ScriptTT e2 r w s p t eff a
triage f x = ScriptTT $ \(s,r) -> \end cont ->
let
end' (z,s1,w) = case z of
Right y -> end (Right y, s1, w)
Left e -> end (Left (f e), s1, w)
in
runScriptTT x (s,r) end' cont
throw
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> e
-> ScriptTT e r w s p t eff a
throw e = ScriptTT $ \(s,r) -> \end cont ->
let end' (_,s1,w1) = end (Left e, s1, w1)
in runScriptTT (return ()) (s,r) end' cont
catch
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> ScriptTT e r w s p t eff a
-> (e -> ScriptTT e r w s p t eff a)
-> ScriptTT e r w s p t eff a
catch (ScriptTT x) h = ScriptTT $ \(s,r) -> \end cont ->
let
end' (z,s1,w) = case z of
Right y -> end (Right y, s1, w)
Left e -> do
let end'' (z2,s2,w2) = end (z2, s2, mappend w w2)
runScriptTT (h e) (s1,r) end'' cont
in
x (s,r) end' cont
prompt
:: (Monoid w, Monad eff, Monad (t eff), MonadTrans t)
=> p a
-> ScriptTT e r w s p t eff a
prompt p = ScriptTT $ \(s,_) -> \end cont ->
cont p (\a -> end (Right a, s, mempty))
instance
( Monoid w, Monad eff, forall m. Monad m => Monad (t m), MonadTrans t
, Arbitrary a, CoArbitrary a
) => Arbitrary (ScriptTT e r w s p t eff a) where
arbitrary = do
(a,b) <- arbitrary :: Gen (a,a)
k <- arbitrary :: Gen Int
if k`rem`2 == 0
then return $ return a
else do
f <- arbitrary :: Gen (a -> ScriptTT e r w s p t eff a)
return $ f a >> lift (return b)
instance Show (ScriptTT e r w s p t eff a) where
show _ = "<Script>"