packages feed

stc-lang-1.0.0: src/Control/Monad/SD/FRP.hs

module Control.Monad.SD.FRP
    ( liftSignal
    , runSignals
    , filterSignalM
    , filterSignal
    , Signals
    ) where

import Control.Monad.Generator
import Control.Monad.SD.Case
import Control.Monad.SD.Ohua
import Control.Monad.SD.STCLang
import Control.Monad.SD.Smap
import Data.Dynamic2
import Data.StateElement

import qualified Control.Concurrent as Conc
import qualified Control.Concurrent.BoundedChan as BC
import Control.DeepSeq (NFData)
import Control.Exception (bracket)
import Control.Monad.State as S
import System.IO (hPutStrLn, stderr)

type Signal = IO

type Signals = (Int, S)

instance Show S where
    show _ = "S"

liftSignal :: (Typeable a, NFData a) => Signal a -> IO a -> STCLang Signals a
liftSignal s0 init = do
    idx <-
        S.state $ \s@CollSt {signals} ->
            (length signals, s {signals = signals ++ [toS <$> s0]})
    liftWithState init $ \(i, s) ->
        if i == idx
            then do
                let my = fromS s
                S.put my
                pure my
            else S.get

debugSignals :: Bool
debugSignals = True

printSignalD :: MonadIO m => String -> m ()
printSignalD
    | debugSignals = liftIO . hPutStrLn stderr
    | otherwise = const $ pure ()

runSignals :: NFData a => STCLang Signals a -> IO ([a], [S])
runSignals comp = do
    printSignalD "Running STCLang"
    (comp', s) <- S.runStateT comp mempty
    chan <- BC.newBoundedChan 100
    bracket
        (do printSignalD "Starting signals... "
            forM (zip [0 ..] $ signals s) $ \(idx, sig) ->
                Conc.forkIO $
                forever $ do
                    event <- sig
                    BC.writeChan chan $ Just (idx, event))
        (\threads -> do
             printSignalD "Killing signal threads"
             mapM_ Conc.killThread threads)
        (\_ -> do
             putStrLn "signals done"
             let signalGen = ioReaderToGenerator (BC.readChan chan)
             runOhuaM (smapGen comp' signalGen) $ states s)

filterSignalM ::
       (Show b, NFData a, NFData b)
    => (a -> OhuaM Bool)
    -> (a -> OhuaM b)
    -> STCLang a (Maybe b)
filterSignalM cond f =
    pure $ \item -> if_ (cond item) (Just <$> f item) (pure Nothing)
    -- | @filter init p f@ applies @f@ to only those values @a@ that satisfy the
    -- predicate @p@. For values not satisfying it returns the last computed value
    -- (initially @init@)

filterSignal ::
       (Show b, Typeable b, NFData b, NFData a)
    => IO b -- Initial value for the output
    -> (a -> OhuaM Bool) -- predicate
    -> (a -> OhuaM b) -- computation to perform on `a`
    -> STCLang a b
filterSignal init cond f = do
    g <- liftWithState init $ maybe S.get (\i -> S.put i >> pure i)
    fil <- filterSignalM cond f
    return $ fil >=> g