{-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-} {- | Utilities for integrating live programs into external loops, using 'IO' concurrency. The basic idea is two wormholes (see Winograd-Court's thesis). -} 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.Strict -- essence-of-live-coding import LiveCoding.Cell import LiveCoding.Cell.Monad.Trans 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 concurrently :: (MonadIO m, Monoid eOut) => 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) -< () (eOut, b) <- 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)