Simulation arrows === Simple update of the simulation arrows from *Programming with Arrows* to work with the modern Arrow libraries. > {-# LANGUAGE Arrows #-} > module Sim where > > -- discrete event simulation library. > -- This time, every channel always carries a value, and an *initial* value must > -- be supplied before simulation starts. > > import Control.Category > import Control.Monad > import Control.Monad.Fix > import Control.Arrow > import Data.IORef > > import Prelude hiding ((.), id) > > type Time = Double > > infinity :: Time > infinity = 1/0 > > data Event a = Event {time::Time, value::a} > > instance Show a => Show (Event a) where > show t = show (value t)++"@"++show (time t) > > -- The simulation arrow: given initial value of input signal, deliver initial > -- value of output signal and a running simulation. > -- invariant: no output event should precede the first input. > > newtype Sim m a b = Sim (a -> m (b, State m a b)) > > sim :: Monad m => (a -> m (b, State m a b)) -> Sim m a b > sim f = Sim $ \a -> do > (b,s) <- f a > return (b,quiescent s) > > quiescent :: Monad m => State m a b -> State m a b > quiescent (Lift m) = Lift (liftM quiescent m) > quiescent (Wait t s k) = wait t (quiescent s) k > quiescent (Ready _ _) = error "Trying to output before first input" > > -- running simulations. > -- invariant: output events are in non-decreasing time order, > -- output events do not precede inputs or timeouts they depend on, > -- enforced by smart constructors > > data State m a b = Ready (Event b) (State m a b) > | Lift (m (State m a b)) > | Wait Time (State m a b) (Event a -> State m a b) > > ready :: Monad m => Event a1 -> State m a a1 -> State m a a1 > ready e r = Ready e (checkSequence ("Ready "++show (time e)) (time e) r) > > lift :: Monad m => m (State m a b) -> State m a b > lift = Lift > > wait :: Monad m => > Time -> State m a b -> (Event a -> State m a b) -> State m a b > wait t f k = Wait t (checkSequence ("Wait "++show t) t f) > (\e -> checkSequence > ("Wait "++show t++" \\"++show (time e)++" ->") > (time e) (k e)) > > -- ensure all outputs occur no earlier than t > > {- > -- checkSequence is a version of causal which maintains a trace of events > -- to report on an eventual failure. Useful for debugging new arrows. > -- If debugging is unnecessary, it can be replaced by causal. > > checkSequence s t (Ready e f) | t <= time e = Ready e f > checkSequence s t (Lift m) = > Lift (liftM (checkSequence (s++"\nLift") t) m) > checkSequence s t (Wait t' f k) = > Wait t' (checkSequence (s++"\nWait "++show t') t f) > (\e -> checkSequence > (s++"\nWait "++show t'++" \\"++show (time e)++" ->") > t (k e)) > checkSequence s t (Ready e f) = > error $ "checkSequence: "++show t++" > "++show (time e)++"\n"++s++ > "\nReady "++show (time e) > -} > > checkSequence :: Monad m => t -> Time -> State m a b -> State m a b > checkSequence _ = causal > > causal :: Monad m => Time -> State m a b -> State m a b > causal t (Ready e f) | t <= time e = Ready e f > | otherwise = error "Violation of causality" > causal t (Lift m) = Lift (liftM (causal t) m) > causal t (Wait t' s k) = Wait t' (causal t s) (causal t.k) > > -- run function supplies initial value and input events, and runs simulation > -- in the underlying monad. > runSim :: Monad m => Sim m t t1 -> t -> [Event t] -> m () > runSim (Sim f) a as = do > (_,r) <- f a > runState r as > > runState :: Monad m => State m t t1 -> [Event t] -> m () > runState (Ready _ s) as = runState s as > runState (Lift m) as = do s <- m > runState s as > runState (Wait t s _) [] > | t==infinity = return () -- infinity never comes > | otherwise = runState s [] -- timeout > runState (Wait t s k) (a:as) > | t <= time a = runState s (a:as) -- timeout > | otherwise = runState (k a) as -- receive event > > -- Transition function when a simulation receives an input > after :: Monad m => State m a a1 -> Event a -> State m a a1 > Ready b s `after` a = ready b (s `after` a) > Lift m `after` a = lift (liftM (`after` a) m) > Wait t s k `after` a > | t <= time a = s `after` a > | otherwise = k a > > instance Monad m => Category (Sim m) where > id = simArr id > (.) = simComp > > instance Monad m => Arrow (Sim m) where > arr = simArr > first = simFirst > > simArr :: Monad m => (a -> b) -> Sim m a b > simArr f = sim $ \a -> return (f a, s) > where s = waitInput (\a -> ready (Event (time a) (f (value a))) s) > > waitInput :: Monad m => (Event a -> State m a b) -> State m a b > waitInput k = wait infinity undefined k > > simComp :: Monad m => Sim m t1 b -> Sim m t t1 -> Sim m t b > Sim g `simComp` Sim f = sim $ \a -> do > (b,sf) <- f a > (c,sg) <- g b > return (c,sf `stateComp` sg) > > stateComp :: Monad m => State m t1 t -> State m t a1 -> State m t1 a1 > sf `stateComp` Ready c sg = ready c (sf `stateComp` sg) > sf `stateComp` Lift m = lift (liftM (sf `stateComp`) m) > Ready b sf `stateComp` sg = sf `stateComp` (sg `after` b) > Lift m `stateComp` sg = lift (liftM (`stateComp` sg) m) > Wait tf sf kf `stateComp` Wait tg sg kg = > wait (min tf tg) timeout (\a -> kf a `stateComp` Wait tg sg kg) > where timeout | tf | tf>tg = Wait tf sf kf `stateComp` sg > | otherwise = sf `stateComp` sg > > simFirst :: Monad m => Sim m a b -> Sim m (a, c) (b, c) > simFirst (Sim f) = sim $ \(a,c) -> do > (b,s) <- f a > return ((b,c), stateFirst b c s) > > stateFirst :: Monad m => b -> c -> State m a b -> State m (a, c) (b, c) > stateFirst b c (Ready b' s) = > wait (time b') > (ready (Event (time b') (value b',c)) (stateFirst (value b') c s)) > (\(Event t' (a,c')) -> > ready (Event t' (b,c')) > (stateFirst b c' (ready b' (s `after` (Event t' a))))) > stateFirst b c (Lift m) = Lift (liftM (stateFirst b c) m) > stateFirst b c (Wait t s k) = > wait t (stateFirst b c s) $ \(Event t' (a,c')) -> > ready (Event t' (b,c')) (stateFirst b c' (k (Event t' a))) > > -- Can we define a loop? > > instance MonadFix m => ArrowLoop (Sim m) where > loop = simLoop > > simLoop :: MonadFix m => Sim m (t, t1) (b, t1) -> Sim m t b > simLoop (Sim f) = sim $ \a -> do > ((b,c),s) <- mfix (\(~((_,c),_)) -> f (a,c)) > return (b,stateLoop a c [] s) > > -- stateLoop a c q s > -- a = initial value of input > -- c = initial value of state > -- q = queue of future state changes > -- s = running simulation (a,c) to (b,c) > -- result is a running simulation from a to b, where state changes are > -- fed back at the appropriate times. > > stateLoop :: Monad m => > a -> t -> [(Time, t)] -> State m (a, t) (b, t) -> State m a b > stateLoop a c q (Ready (Event t (b,c')) s) = > ready (Event t b) (stateLoop a c (q++[(t,c')]) s) > stateLoop a c q (Lift m) = lift $ liftM (stateLoop a c q) m > stateLoop a c ((t',c'):q) (Wait t s k) = > wait (min t t') timeout $ \(Event t'' a') -> > stateLoop a' c ((t',c'):q) (k (Event t'' (a',c))) > where timeout | t' | t'>t = stateLoop a c ((t',c'):q) s > | otherwise = stateLoop a c' q (s `after` Event t (a,c')) > stateLoop a c [] (Wait t s k) = > wait t (stateLoop a c [] s) $ \(Event t' a') -> > stateLoop a' c [] (k (Event t' (a',c))) > > -- arrM lifts a monadic function into a Sim arrow. > arrM :: Monad m => (a -> m b) -> Sim m a b > arrM f = sim $ \a -> do > b <- f a > return (b,s) > where s = waitInput $ \(Event t a) -> lift $ do > b <- f a > return (ready (Event t b) s) > > --printA prints all events that pass through > printA :: Show b => [Char] -> Sim IO b b > printA name = sim $ \a -> do > message (show a++"@init") > return (a,s) > where s = waitInput $ \a -> Lift $ do > message (show a) > return (ready a s) > message a = if null name then putStrLn a else putStrLn (name++": "++a) > > --delay1 d delays events by d, removing events at the same time > delay1 :: Monad m => Time -> Sim m b b > delay1 d = sim (\a -> return (a,r)) > where r = waitInput go > go (Event t a) = > wait (t+d) (ready (Event (t+d) a) r) $ \(Event t' a') -> > if t==t' > then go (Event t' a') > else ready (Event (t+d) a) (go (Event t' a')) > > initially :: Monad m => b -> Sim m t b -> Sim m t b > initially x (Sim f) = Sim $ \a -> do (_,s) <- f a > return (x,s) > > --nubA filters out events that repeat values > nubA :: (Eq a, Monad m) => Sim m a a > nubA = sim $ \a -> return (a,go a) > where go a = waitInput $ \(Event t a') -> > if a==a' then go a else ready (Event t a') (go a') > > -- cutoff t s stops a simulation after time t > cutoff :: Monad m => Time -> Sim m t b -> Sim m t b > cutoff t (Sim f) = sim $ \a -> do > (b,r) <- f a > return (b, cutoffState t r) > > cutoffState :: Monad m => > Time -> State m a a1 -> State m a a1 > cutoffState t (Ready b s) > | time b<=t = ready b (cutoffState t s) > | otherwise = stop > where stop = waitInput (const stop) > cutoffState t (Lift m) = lift (liftM (cutoffState t) m) > cutoffState t (Wait t' s k) > | t'<=t = wait t' (cutoffState t s) (cutoffState t.k) > | otherwise = wait infinity undefined (cutoffState t.k) > > -- Experiments with arrow notation > > nor :: Monad m => Sim m (Bool,Bool) Bool > nor = proc (a,b) -> do > (a',b') <- delay1 0.1 -< (a,b) > returnA -< not (a'||b') > > afix :: (MonadFix m, Eq b) => Sim m (a,b) b -> Sim m a b > afix f = loop (f >>> nubA >>> arr id &&& arr id) >>> nubA > > flipflop :: MonadFix m => Sim m (Bool,Bool) (Bool,Bool) > flipflop = proc (reset,set) -> > (|afix (\ ~(x,y)->do > x' <- initially False nor -< (reset,y) > y' <- initially True nor -< (set,x) > returnA -< (x',y'))|) > > oscillator :: MonadFix m => Sim m Bool Bool > oscillator = proc enable -> > (|afix (\x -> nor -< (enable,x))|) > > > -- probe counts the transitions on a channel > -- this is useful for estimating power consumption > > probe :: Metric a => String -> (Sim IO a a -> IO b) -> IO b > probe s k = do r <- newIORef 0 > ans <- k (probeArr r) > n <- readIORef r > putStrLn (s++": "++show n++" transitions") > return ans > where probeArr r = sim $ \a -> return (a, stateProbe r a) > stateProbe r a = waitInput $ \(Event t b) -> > lift $ do > modifyIORef r (+distance a b) > return (ready (Event t b) (stateProbe r b)) > > class Metric a where > distance :: a -> a -> Double > bound :: a -> Double -- the distance between any two points is below bound > -- bound does not evaluate its argument > > instance Metric Bool where > distance a b = if a==b then 0 else 1 > bound _ = 1 > > instance (Metric a, Metric b) => Metric (a,b) where > distance (a,b) (c,d) = distance a c+distance b d > bound ~(a,b)= bound a + bound b > > instance (Metric a, Metric b) => Metric (Either a b) where > distance (Left a) (Left a') = distance a a' > distance (Right b) (Right b') = distance b b' > distance x _ = 1 + (bound a `max` bound b) > where Left a = x > Right b = x > bound x = 1 + (bound l `max` bound r) > where Left l = x > Right r = x > > instance Metric a => Metric [a] where > distance [] [] = 0 > distance (x:xs) (y:ys) = distance x y + distance xs ys > distance [] (y:ys) = (bound y+1)*(fromInteger (toInteger (length ys))+1) > distance xs [] = distance [] xs > bound _ = infinity