{-# LANGUAGE Rank2Types, TupleSections, ScopedTypeVariables #-}
module Control.Monad.Script (
Script
, execScript
, execScriptM
, ScriptT()
, execScriptT
, execScriptTM
, lift
, except
, triage
, throw
, catch
, ask
, local
, transport
, reader
, tell
, draft
, listen
, pass
, censor
, get
, put
, modify
, modify'
, gets
, prompt
, checkScript
, checkScriptM
, checkScriptT
, checkScriptTM
) where
import Control.Monad
( ap, join )
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 )
newtype ScriptT e r w s p m a = ScriptT
{ runScriptT
:: (s,r)
-> forall v.
((Either e a, s, w) -> m v)
-> (forall u. p u -> (u -> m v) -> m v)
-> m v
} deriving Typeable
instance (Monoid w) => Monad (ScriptT e r w s p m) where
return x = ScriptT $ \(s,_) -> \end _ -> end (Right x, s, mempty)
x >>= f = ScriptT $ \(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)
runScriptT (f y) (s1,r) h cont
Left e -> do
let h (_,s2,w2) = end (Left e, s2, mappend w1 w2)
runScriptT (return ()) (s1,r) h cont
runScriptT x (s0,r) g cont
instance (Monoid w) => Applicative (ScriptT e r w s p m) where
pure = return
(<*>) = ap
instance (Monoid w) => Functor (ScriptT e r w s p m) where
fmap f x = x >>= (return . f)
type Script e r w s p = ScriptT e r w s p Identity
execScriptTC
:: s
-> r
-> ((Either e a, s, w) -> m v)
-> (forall u. p u -> (u -> m v) -> m v)
-> ScriptT e r w s p m a
-> m v
execScriptTC s r end cont x =
runScriptT x (s,r) end cont
execScriptT
:: (Monad m)
=> s
-> r
-> (forall u. p u -> u)
-> ScriptT e r w s p m t
-> m (Either e t, s, w)
execScriptT s r eval =
execScriptTC s r return (\p c -> c $ eval p)
checkScriptT
:: (Monad m)
=> s
-> r
-> (forall u. p u -> u)
-> (m (Either e t, s, w) -> IO q)
-> (q -> Bool)
-> ScriptT e r w s p m t
-> Property
checkScriptT s r eval cond check script = monadicIO $ do
let result = execScriptT s r eval script
q <- run $ cond result
assert $ check q
execScriptTM
:: (Monad (m eff), Monad eff)
=> s
-> r
-> (forall u. p u -> eff u)
-> (forall u. eff u -> m eff u)
-> ScriptT e r w s p (m eff) t
-> m eff (Either e t, s, w)
execScriptTM s r eval lift =
execScriptTC s r return
(\p c -> (lift $ eval p) >>= c)
checkScriptTM
:: (Monad (m eff), Monad eff)
=> s
-> r
-> (forall u. p u -> eff u)
-> (forall u. eff u -> m eff u)
-> (m eff (Either e t, s, w) -> IO q)
-> (q -> Bool)
-> ScriptT e r w s p (m eff) t
-> Property
checkScriptTM s r eval lift cond check script = monadicIO $ do
let result = execScriptTM s r eval lift script
q <- run $ cond result
assert $ check q
execScriptC
:: s
-> r
-> ((Either e a, s, w) -> v)
-> (forall u. p u -> (u -> v) -> v)
-> Script e r w s p a
-> v
execScriptC s r end cont x =
let cont' p c = Identity $ cont p (runIdentity . c) in
runIdentity $ runScriptT x (s,r) (Identity . end) cont'
execScript
:: s
-> r
-> (forall u. p u -> u)
-> Script e r w s p t
-> (Either e t, s, w)
execScript s r eval =
execScriptC s r id (\p c -> c $ eval p)
checkScript
:: s
-> r
-> (forall u. p u -> u)
-> ((Either e t, s, w) -> q)
-> (q -> Bool)
-> Script e r w s p t
-> Bool
checkScript s r eval cond check script =
check $ cond $ execScript s r eval script
execScriptM
:: (Monad eff)
=> s
-> r
-> (forall u. p u -> eff u)
-> Script e r w s p t
-> eff (Either e t, s, w)
execScriptM s r eval =
execScriptC s r return
(\p c -> (eval p) >>= c)
checkScriptM
:: (Monad eff)
=> s
-> r
-> (forall u. p u -> eff u)
-> (eff (Either e t, s, w) -> IO q)
-> (q -> Bool)
-> Script e r w s p t
-> Property
checkScriptM s r eval cond check script = monadicIO $ do
let result = execScriptM s r eval script
q <- run $ cond result
assert $ check q
ask
:: (Monoid w)
=> ScriptT e r w s p m r
ask = ScriptT $ \(s,r) -> \end _ ->
end (Right r, s, mempty)
local
:: (r -> r)
-> ScriptT e r w s p m a
-> ScriptT e r w s p m a
local = transport
transport
:: (r2 -> r1)
-> ScriptT e r1 w s p m a
-> ScriptT e r2 w s p m a
transport f x = ScriptT $ \(s,r) -> \end cont ->
runScriptT x (s, f r) end cont
reader
:: (Monoid w)
=> (r -> a)
-> ScriptT e r w s p m a
reader f = fmap f ask
get
:: (Monoid w)
=> ScriptT e r w s p m s
get = ScriptT $ \(s,_) -> \end _ ->
end (Right s, s, mempty)
put
:: (Monoid w)
=> s
-> ScriptT e r w s p m ()
put s = ScriptT $ \(_,_) -> \end _ ->
end (Right (), s, mempty)
modify
:: (Monoid w)
=> (s -> s)
-> ScriptT e r w s p m ()
modify f = ScriptT $ \(s,_) -> \end _ ->
end (Right (), f s, mempty)
modify'
:: (Monoid w)
=> (s -> s)
-> ScriptT e r w s p m ()
modify' f = ScriptT $ \(s,_) -> \end _ ->
end (Right (), f $! s, mempty)
gets
:: (Monoid w)
=> (s -> a)
-> ScriptT e r w s p m a
gets f = ScriptT $ \(s,_) -> \end _ ->
end (Right (f s), s, mempty)
tell
:: w
-> ScriptT e r w s p m ()
tell w = ScriptT $ \(s,_) -> \end _ ->
end (Right (), s, w)
draft
:: (Monoid w)
=> ScriptT e r w s p m a
-> ScriptT e r w s p m (a,w)
draft x = ScriptT $ \(r,s) -> \end cont ->
runScriptT x (r,s)
(\(y,s,w) -> end (fmap (,w) y, s, mempty)) cont
listen
:: ScriptT e r w s p m a
-> ScriptT e r w s p m (a,w)
listen x = ScriptT $ \(r,s) -> \end cont ->
runScriptT x (r,s)
(\(y,s,w) -> end (fmap (,w) y, s, w)) cont
pass
:: ScriptT e r w s p m (a, w -> w)
-> ScriptT e r w s p m a
pass x = ScriptT $ \(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
runScriptT x (r,s) end' cont
censor
:: (w -> w)
-> ScriptT e r w s p m a
-> ScriptT e r w s p m a
censor f x = pass $ ScriptT $ \(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
runScriptT x (s,r) end' cont
except
:: (Monoid w)
=> Either e a
-> ScriptT e r w s p m a
except z = ScriptT $ \(s,_) -> \end _ ->
end (z, s, mempty)
triage
:: (Monoid w)
=> (e1 -> e2)
-> ScriptT e1 r w s p m a
-> ScriptT e2 r w s p m a
triage f x = ScriptT $ \(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
runScriptT x (s,r) end' cont
throw
:: (Monoid w)
=> e
-> ScriptT e r w s p m a
throw e = ScriptT $ \(s,r) -> \end cont ->
let end' (_,s1,w1) = end (Left e, s1, w1)
in runScriptT (return ()) (s,r) end' cont
catch
:: (Monoid w)
=> ScriptT e r w s p m a
-> (e -> ScriptT e r w s p m a)
-> ScriptT e r w s p m a
catch x h = ScriptT $ \(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)
runScriptT (h e) (s1,r) end'' cont
in
runScriptT x (s,r) end' cont
prompt
:: (Monoid w)
=> p a
-> ScriptT e r w s p m a
prompt p = ScriptT $ \(s,_) -> \end cont ->
cont p (\a -> end (Right a, s, mempty))
lift
:: (Monoid w, Monad m)
=> m a
-> ScriptT e r w s p m a
lift x = ScriptT $ \(s,_) -> \end _ ->
x >>= \a -> end (Right a, s, mempty)
instance (Monad m, Monoid w, Arbitrary a, CoArbitrary a)
=> Arbitrary (ScriptT e r w s p m 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 -> ScriptT e r w s p m a)
return $ f a >> lift (return b)
instance Show (ScriptT e r w s p m a) where
show _ = "<Script>"