titan-record-yampa-1.0.0: src/FRP/Titan/Record/Yampa.hs
{-# LANGUAGE ScopedTypeVariables #-}
-- | Replacement of Yampa's reactimate function with recod-and-replay
-- capabilities.
module FRP.Titan.Record.Yampa
( reactimateRecord
, RecordMode(..)
)
where
import Control.Exception
import Control.Monad
import Data.IORef
import Data.Maybe
import FRP.Yampa
import System.IO
-- | How to treat the given trace: read (replay) but not write to it,
-- write (record) but not replay it, and readwrite (replay until the end
-- and the continue recording to it).
data RecordMode = RecordReadOnly
| RecordWriteOnly
| RecordReadWrite
deriving (Eq, Show)
recordMustWrite RecordReadOnly = False
recordMustWrite _ = True
recordMustRead RecordWriteOnly = False
recordMustRead _ = True
reactimateRecord :: (Read a, Show a)
=> Maybe (FilePath, RecordMode) -- ^ Debug: File onto which the result should be recorded and recording mode
-> IO a -- ^ FRP: Initial sensing action
-> (Bool -> IO (DTime, Maybe a)) -- ^ FRP: Continued sensing action
-> (Bool -> b -> IO Bool) -- ^ FRP: Rendering/consumption action
-> SF a b -- ^ FRP: Signal Function that defines the program
-> IO ()
reactimateRecord Nothing sense0 sense actuate sf = reactimate sense0 sense actuate sf
reactimateRecord (Just (fp, mode)) sense0 sense actuate sf = do
samples <- (maybeRead =<<) <$> (catch (Just <$> readFile fp) (\(e :: IOException) -> return Nothing))
hPutStrLn stderr (show samples)
-- Read from here
sample0Ref <- newIORef (fmap fst samples)
samplesRef <- newIORef (let ss = fromMaybe [] $ fmap snd samples in length ss `seq` ss)
-- Write into here
newSample0Ref <- newIORef Nothing
newSamplesRef <- newIORef []
let newSense0 = do
-- Sense from IO
a <- sense0
-- Choose which one to use
a' <- if recordMustRead mode
then do sample0' <- readIORef sample0Ref
return (fromMaybe a sample0')
else return a
-- Update record, if necessary
when (recordMustWrite mode) $ writeIORef newSample0Ref (Just a')
return a'
let newSense b = do
as <- sense b
as' <- if recordMustRead mode
then do sample <- readIORef samplesRef
case sample of
[] -> return as
(x:xs) -> do writeIORef samplesRef xs
return x
else return as
when (recordMustWrite mode) $ do
curSamples <- readIORef newSamplesRef
writeIORef newSamplesRef (curSamples ++ [as'])
return as'
let newActuate x y = do
last <- actuate x y
when (recordMustWrite mode) $ do
curSample0 <- readIORef newSample0Ref
curSamples <- readIORef newSamplesRef
case curSample0 of
Nothing -> return ()
Just s0 -> length curSamples `seq` writeFile fp (show (s0, curSamples))
return last
(maybe 0 (length.snd) samples) `seq`
reactimate newSense0 newSense newActuate sf
maybeRead :: Read a => String -> Maybe a
maybeRead = fmap fst . listToMaybe . reads