reactive-banana-1.3.2.0: src/Reactive/Banana/Prim/Mid/Compile.hs
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-----------------------------------------------------------------------------
reactive-banana
------------------------------------------------------------------------------}
module Reactive.Banana.Prim.Mid.Compile where
import Control.Exception
( evaluate )
import Data.Functor
( void )
import Data.IORef
( newIORef, readIORef, writeIORef )
import qualified Reactive.Banana.Prim.Low.GraphGC as GraphGC
import qualified Reactive.Banana.Prim.Low.OrderedBag as OB
import Reactive.Banana.Prim.Mid.Combinators (mapP)
import Reactive.Banana.Prim.Mid.Evaluation (applyDependencyChanges)
import Reactive.Banana.Prim.Mid.IO
import Reactive.Banana.Prim.Mid.Plumbing
import Reactive.Banana.Prim.Mid.Types
{-----------------------------------------------------------------------------
Compilation
------------------------------------------------------------------------------}
-- | Change a 'Network' of pulses and latches by
-- executing a 'BuildIO' action.
compile :: BuildIO a -> Network -> IO (a, Network)
compile m Network{nTime, nOutputs, nAlwaysP, nGraphGC} = do
(a, dependencyChanges, os) <- runBuildIO (nTime, nAlwaysP) m
applyDependencyChanges dependencyChanges nGraphGC
let state2 = Network
{ nTime = next nTime
, nOutputs = OB.inserts nOutputs os
, nAlwaysP
, nGraphGC
}
return (a,state2)
emptyNetwork :: IO Network
emptyNetwork = do
(alwaysP, _, _) <- runBuildIO undefined $ newPulse "alwaysP" (return $ Just ())
nGraphGC <- GraphGC.new
pure Network
{ nTime = next beginning
, nOutputs = OB.empty
, nAlwaysP = alwaysP
, nGraphGC
}
{-----------------------------------------------------------------------------
Testing
------------------------------------------------------------------------------}
-- | Simple interpreter for pulse/latch networks.
--
-- Mainly useful for testing functionality
--
-- Note: The result is not computed lazily, for similar reasons
-- that the 'sequence' function does not compute its result lazily.
interpret :: (Pulse a -> BuildIO (Pulse b)) -> [Maybe a] -> IO [Maybe b]
interpret f xs = do
o <- newIORef Nothing
let network = do
(pin, sin) <- liftBuild newInput
pmid <- f pin
pout <- liftBuild $ mapP return pmid
liftBuild $ addHandler pout (writeIORef o . Just)
return sin
-- compile initial network
(sin, state) <- compile network =<< emptyNetwork
let go Nothing s1 = return (Nothing,s1)
go (Just a) s1 = do
(reactimate,s2) <- sin a s1
reactimate -- write output
ma <- readIORef o -- read output
writeIORef o Nothing
return (ma,s2)
fst <$> mapAccumM go state xs -- run several steps
-- | Execute an FRP network with a sequence of inputs.
-- Make sure that outputs are evaluated, but don't display their values.
--
-- Mainly useful for testing whether there are space leaks.
runSpaceProfile :: Show b => (Pulse a -> BuildIO (Pulse b)) -> [a] -> IO ()
runSpaceProfile f xs = do
let g = do
(p1, fire) <- liftBuild newInput
p2 <- f p1
p3 <- mapP return p2 -- wrap into Future
addHandler p3 (void . evaluate)
return fire
(step,network) <- compile g =<< emptyNetwork
let fire x s1 = do
(outputs, s2) <- step x s1
outputs -- don't forget to execute outputs
return ((), s2)
mapAccumM_ fire network xs
-- | 'mapAccum' for a monad.
mapAccumM :: Monad m => (a -> s -> m (b,s)) -> s -> [a] -> m ([b],s)
mapAccumM f s0 = go s0 []
where
go s1 bs [] = pure (reverse bs,s1)
go s1 bs (x:xs) = do
(b,s2) <- f x s1
go s2 (b:bs) xs
-- | Strict 'mapAccum' for a monad. Discards results.
mapAccumM_ :: Monad m => (a -> s -> m (b,s)) -> s -> [a] -> m ()
mapAccumM_ _ _ [] = return ()
mapAccumM_ f !s0 (x:xs) = do
(_,s1) <- f x s0
mapAccumM_ f s1 xs