{- | Utilities for integrating live programs into external loops, using 'IO' concurrency. The basic idea is two wormholes (see Winograd-Court's thesis). -} {-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-} module LiveCoding.External where -- base import Control.Arrow import Control.Concurrent import Control.Monad.IO.Class -- transformers import Control.Monad.Trans.Reader import Control.Monad.Trans.Writer -- essence-of-live-coding import LiveCoding.Cell import LiveCoding.Exceptions type ExternalCell m eIn eOut a b = Cell (ReaderT eIn (WriterT eOut m)) a b type ExternalLoop eIn eOut = Cell IO eIn eOut runWriterC :: Monad m => Cell (WriterT w m) a b -> Cell m a (b, w) runWriterC Cell { .. } = Cell { cellStep = \state a -> fmap (\((b, w), s) -> ((b, s), w)) $ runWriterT $ cellStep state a , .. } concurrently :: MonadIO m => ExternalCell m eIn eOut a b -> IO (Cell m a b, ExternalLoop eIn eOut) concurrently externalCell = do inVar <- newEmptyMVar outVar <- newEmptyMVar let cell = proc a -> do eIn <- constM (liftIO $ takeMVar inVar) -< () (b, eOut) <- runWriterC (runReaderC' externalCell) -< (eIn, a) arrM (liftIO . putMVar outVar) -< eOut returnA -< b externalLoop = arrM (putMVar inVar) >>> constM (takeMVar outVar) return (cell, externalLoop) type CellHandle a b = MVar (Cell IO a b) makeHandle :: Cell IO a b -> IO (CellHandle a b) makeHandle = newMVar stepHandle :: CellHandle a b -> a -> IO b stepHandle handle a = modifyMVar handle $ \cell -> do (b, cell') <- step cell a return (cell', b)