box-0.0.1.1: test/ctest.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedLabels #-}
{-# OPTIONS_GHC -Wall #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
-- | dejavu testing
--
module Main where
import Control.Category (id)
import Control.Monad.Conc.Class as C
import Control.Concurrent.Classy.STM as C
import Box.Box
import Box.Committer
import Box.Emitter
import Box.Broadcast
import Box.Connectors
import Box.Cont
import Box.IO
import Box.Queue
import Box.Stream
import Box.Transducer
import Data.String
import Protolude hiding (STM)
import Test.DejaFu
import Test.DejaFu.Conc
import qualified Streaming.Prelude as S
import System.Random
import Control.Lens hiding ((:>), (.>), (<|), (|>))
import Control.Concurrent.Classy.Async as C
import qualified Control.Monad.Trans.State as Trans
import Data.Generics.Labels ()
import Data.Generics.Product
-- | the test box is a pure list emitter into an IORef appending list
testBox :: (MonadConc m) => Int -> m (Cont m (Box (STM m) Int Int), m [Int])
testBox n = do
(_, c, res) <- cCRef
let e = toEmit (S.take n $ S.each [0..])
pure (Box <$> c <*> e, res)
-- | fuse
exFuse :: (MonadConc m) => Int -> m [Int]
exFuse n = do
(b, res) <- testBox n
fuse (pure . pure) (liftB <$> b)
res
exFuseSTM :: (MonadConc m) => Int -> m [Int]
exFuseSTM n = do
(b, res) <- testBox n
fuseSTM (pure . pure) b
res
exEtc :: (MonadConc m) => Int -> m [Int]
exEtc n = do
(b, res) <- testBox n
etc () (Transducer id) b
res
-- | one emitter and 2 committers - STM fusion
e1c2 :: (MonadConc m) => Cont m (Emitter (STM m) b) -> m ([b],[b])
e1c2 e = do
(_,c1,r1) <- cCRef
(_,c2,r2) <- cCRef
fuseSTM (pure . pure) (Box <$> c1 <*> e)
fuseSTM (pure . pure) (Box <$> c2 <*> e)
(,) <$> r1 <*> r2
exe1c2 :: (MonadConc m) => Int -> m ([Int],[Int])
exe1c2 n = e1c2 (toEmit (S.take n $ S.each [0..]))
-- | one emitter and 2 committers - IO fusion
e1c2IO :: (MonadConc m) => Cont m (Emitter (STM m) b) -> m ([b],[b])
e1c2IO e = do
(_,c1,r1) <- cCRef
(_,c2,r2) <- cCRef
fuse (pure . pure) (liftB <$> (Box <$> c1 <*> e))
fuse (pure . pure) (liftB <$> (Box <$> c2 <*> e))
(,) <$> r1 <*> r2
exe1c2IO :: (MonadConc m) => Int -> m ([Int],[Int])
exe1c2IO n = e1c2IO (toEmit (S.take n $ S.each [0..]))
-- | test when the deterministic takes too long (which is almost always)
t :: (MonadConc n, MonadIO n, Eq b, Show b) =>
String -> ConcT n b -> n Bool
t l c = dejafuWay (randomly (mkStdGen 42) 1000) defaultMemType l alwaysSame c
main :: IO ()
main = do
let n = 2
sequence_ $ autocheck <$> [exFuse n, exFuseSTM n, exEtc n]
void $ t "e1c2" (exe1c2 10)
void $ t "e1c2 IO" (exe1c2IO 10)
exc :: (MonadConc m) => Int -> m ([Int],[Int])
exc n = do
ref <- newIORef 0
let e = Emitter $ do
a <- readIORef ref
if a < n
then do
writeIORef ref (a+1)
pure (Just a)
else pure Nothing
(_,c1,r1) <- cCRef
(_,c2,r2) <- cCRef
fuse (pure . pure) (Box <$> (liftC <$> c1) <*> pure e)
fuse (pure . pure) (Box <$> (liftC <$> c2) <*> pure e)
(,) <$> r1 <*> r2
eCounter :: (C.MonadConc m) => Int -> Int -> m (Emitter m Int, IORef m Int)
eCounter start n = do
ref <- newIORef start
pure (
Emitter $ do
a <- readIORef ref
if a < n
then do
writeIORef ref (a+1)
pure (Just a)
else pure Nothing, ref)
eCounter' :: (C.MonadConc m) => Int -> Int -> m (Cont m (Emitter m Int), IORef m Int)
eCounter' start n = do
ref <- newIORef start
pure ( fuseEmitM $
Emitter $ do
a <- readIORef ref
if a < n
then do
writeIORef ref (a+1)
pure (Just a)
else pure Nothing, ref)
exc' :: (MonadIO m, MonadConc m) => Int -> m ([Int],[Int])
exc' n = do
(b, c) <- broadcast'
(ec, eref) <- eCounter 0 n
let e1 = subscribe' b
let e2 = subscribe' b
fuse' (pure . pure) (pure $ Box c ec)
(_,c1,r1) <- cCRef
(_,c2,r2) <- cCRef
fuse (pure . pure) (Box <$> (liftC <$> c1) <*> e1)
fuse (pure . pure) (Box <$> (liftC <$> c2) <*> e2)
eres <- readIORef eref
putStrLn $ "eref: " <> (show eres :: Text)
(,) <$> r1 <*> r2
-- | a broadcaster
newtype Broadcaster' m a = Broadcaster'
{ unBroadcast :: TVar (STM m) (Committer m a)
}
-- | create a (broadcaster, committer)
broadcast' :: (Show a, MonadConc m, MonadIO m) => m (Broadcaster' m a, Committer m a)
broadcast' = do
ref <- C.atomically $ newTVar mempty
let com = Committer $ \a -> do
putStrLn $ "broadcaster': " <> (show a :: Text)
c <- C.atomically $ readTVar ref
commit c a
pure (Broadcaster' ref, com)
-- | subscribe to a broadcaster
subscribe' :: (Show a, MonadIO m, MonadConc m) => Broadcaster' m a -> Cont m (Emitter m a)
subscribe' (Broadcaster' tvar) = Cont $ \e -> queueELog cio e
where
cio c = C.atomically $ modifyTVar' tvar (mappend c)
-- * primitives
-- | fuse an emitter directly to a committer
fuse_' :: (Show a, MonadIO m) => Emitter m a -> Committer m a -> m ()
fuse_' e c = go
where
go = do
a <- emit e
putStrLn $ "fuse_' emit: " <> (show a :: Text)
c' <- maybe (pure False) (commit c) a
putStrLn $ "fuse_' commit: " <> (show c' :: Text)
when c' go
fuse' :: (Show b, MonadIO m) => (a -> m (Maybe b)) -> Cont m (Box m b a) -> m ()
fuse' f box = with box $ \(Box c e) -> fuse_' (emap f e) c
-- exEmerge :: (MonadIO m, MonadConc m) => Int -> Int -> Int -> Int -> m [Int]
exEmerge :: MonadConc m => Int -> Int -> Int -> Int -> m ([Int], Int, Int)
exEmerge st1 st2 n1 n2 = do
(e1, eref1) <- eCounter st1 n1
(e2, eref2) <- eCounter st2 n2
(_,c1,r1) <- cCRef
fuse (pure . pure) $ Box <$> (liftC <$> c1) <*> emergeM (pure (e1, e2))
(,,) <$> r1 <*> readIORef eref1 <*> readIORef eref2
exEmergeM :: MonadConc m => Int -> Int -> Int -> Int -> m ([Int], Int, Int)
exEmergeM st1 st2 n1 n2 = do
(e1, eref1) <- eCounter' st1 n1
(e2, eref2) <- eCounter' st2 n2
(_,c1,r1) <- cCRef
fuse (pure . pure) $ Box <$> (liftC <$> c1) <*> emergeM ((,) <$> e1 <*> e2)
(,,) <$> r1 <*> readIORef eref1 <*> readIORef eref2
temerge :: IO Bool
temerge = dejafuWay
(randomly (mkStdGen 42) 1000)
defaultMemType
"test emergeM"
alwaysSame
(exEmergeM 0 0 2 2)
exCSplit :: MonadConc m => Int -> Int -> m [Int]
exCSplit st1 n1 = do
(e1, _) <- eCounter' st1 n1
(_,c1,r1) <- cCRef
fuse (pure . pure) $ Box <$> (liftC <$> liftA2 (<>) c1 c1) <*> e1
r1
contCommit' :: Either (Committer m Int) (Committer m Int) -> Committer m Int
contCommit' ec =
Committer $ \a ->
case ec of
Left lc -> commit (contramap (100+) lc) a
Right rc -> commit rc a
splitCommitM :: (MonadConc m) =>
Cont m (Committer m a)
-> Cont m (Either (Committer m a) (Committer m a))
splitCommitM c =
Cont $ \kk ->
with c $ \c' ->
fst <$>
C.concurrently
(queueCM (kk . Left) (`fuse_` c'))
(queueCM (kk . Right) (`fuse_` c'))
counter :: (MonadState Int m) => Int -> StateT Int m (Emitter m Int)
counter n =
pure $
Emitter $ do
a <- Protolude.get
case a < n of
False -> pure Nothing
True -> do
put $ a + 1
pure (Just a)
counterT :: (Num a, Ord a, Monad m) => a -> Emitter (StateT a m) a
counterT n =
Emitter $ do
a <- Trans.get
case a < n of
False -> pure Nothing
True -> do
Trans.put $ a + 1
pure (Just a)
rememberer :: (MonadState [Int] m) => StateT [Int] m (Committer m Int)
rememberer =
pure $
Committer $ \a -> do
modify (a:)
pure True
remembererT :: (Monad m) => Committer (StateT [a] m) a
remembererT =
Committer $ \a -> do
Trans.modify (a:)
pure True
data StateExs = StateExs { count :: Int, result :: [Int]} deriving (Show, Eq, Generic)
boxCount ::
(MonadConc m, MonadState StateExs m) =>
Int ->
m (Box m Int Int)
boxCount n = Box <$> pure rememberer' <*> pure counter' where
counter' =
Emitter $ do
a <- use #count
case a < n of
False -> pure Nothing
True -> do
#count += 1
pure (Just a)
rememberer' =
Committer $ \a -> do
#result %= (a:)
pure True
countEmitter :: (Ord a, Num a, MonadState s m, Data.Generics.Product.HasField "count" s s a a) => a -> Emitter m a
countEmitter n = Emitter $ do
a <- use (field @"count")
case a < n of
False -> pure Nothing
True -> do
field @"count" += 1
pure (Just a)
resultCommitter :: (MonadState s m, Data.Generics.Product.HasField "result" s s [a] [a]) => Committer m a
resultCommitter = Committer $ \a -> do
field @"result" %= (a:)
pure True
-- boxCount' :: (MonadState StateExs m, MonadConc m) => Int -> Box m Int Int
boxCount' n = Box (zoom #result resultCommitter) (zoom #count (countEmitter n))
exs :: (MonadConc m) => Int -> m [Int]
exs n = do
(StateExs _ res) <- execStateT
(fuse (pure . pure) (pure $ Box resultCommitter (countEmitter n)))
(StateExs 0 [])
pure (reverse res)
fuse'' :: (Show b, MonadIO m) => (a -> m (Maybe b)) -> Cont m (Box m b a) -> m ()
fuse'' f box = with box $ \(Box c e) -> fuse_' (emap f e) c
-- | emitter hooked into broadcasting is broken ...
-- > exbr 2
-- ([],[])
--
broadcasting :: MonadConc m => Cont m (Emitter (STM m) b) -> m ([b], [b])
broadcasting e = do
(b, c) <- C.atomically broadcast
let e1 = subscribe b
let e2 = subscribe b
fuseSTM (pure . pure) (Box <$> pure c <*> e)
(_,c1,r1) <- cCRef
(_,c2,r2) <- cCRef
fuseSTM (pure . pure) (Box <$> c1 <*> e1)
fuseSTM (pure . pure) (Box <$> c2 <*> e2)
(,) <$> r1 <*> r2
exbrPure :: (MonadConc m) => Int -> m ([Int],[Int])
exbrPure n = broadcasting (toEmit (S.take n $ S.each [0..]))
-- > exbrIO 2
-- 1
-- 2
-- ([],[])
--
exbrIO :: Int -> IO ([Text],[Text])
exbrIO n = broadcasting (eStdin n)