packages feed

essence-of-live-coding 0.2.6 → 0.2.7

raw patch · 43 files changed

+1547/−1108 lines, 43 filessetup-changedPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- LiveCoding: [Handling] :: {key :: Key, handle :: h} -> Handling h
- LiveCoding: [Uninitialized] :: Handling h
- LiveCoding.HandlingState: [Handling] :: {key :: Key, handle :: h} -> Handling h
- LiveCoding.HandlingState: [Uninitialized] :: Handling h
- LiveCoding.HandlingState: dataTypeHandling :: DataType
- LiveCoding.HandlingState: handlingConstr :: Constr
- LiveCoding.HandlingState: instance Data.Typeable.Internal.Typeable h => Data.Data.Data (LiveCoding.HandlingState.Handling h)
- LiveCoding.HandlingState: uninitializedConstr :: Constr
+ LiveCoding: Handling :: Key -> h -> Handling h
+ LiveCoding: Initialized :: a -> NoMigration a
+ LiveCoding: Uninitialized :: NoMigration a
+ LiveCoding: [handle] :: Handling h -> h
+ LiveCoding: [key] :: Handling h -> Key
+ LiveCoding: arrChangesM :: (Monad m, Typeable a, Typeable b, Eq a) => (a -> m b) -> Cell m a b
+ LiveCoding: cellNoMigration :: (Typeable s, Functor m) => s -> (s -> a -> m (b, s)) -> Cell m a b
+ LiveCoding: data NoMigration a
+ LiveCoding: dataTypeNoMigration :: DataType
+ LiveCoding: fromNoMigration :: a -> NoMigration a -> a
+ LiveCoding: initializedConstr :: Constr
+ LiveCoding: mkConstrTag :: DataType -> String -> Int -> [String] -> Fixity -> Constr
+ LiveCoding: resampleListPar :: Monad m => Cell m a b -> Cell m [a] [b]
+ LiveCoding: trLiftedRep :: TypeRep ('BoxedRep 'Lifted)
+ LiveCoding: uninitializedConstr :: Constr
+ LiveCoding.Cell.Resample: resampleListPar :: Monad m => Cell m a b -> Cell m [a] [b]
+ LiveCoding.HandlingState: Handling :: Key -> h -> Handling h
+ LiveCoding.HandlingState: [handle] :: Handling h -> h
+ LiveCoding.HandlingState: [key] :: Handling h -> Key
+ LiveCoding.Migrate.NoMigration: Initialized :: a -> NoMigration a
+ LiveCoding.Migrate.NoMigration: Uninitialized :: NoMigration a
+ LiveCoding.Migrate.NoMigration: arrChangesM :: (Monad m, Typeable a, Typeable b, Eq a) => (a -> m b) -> Cell m a b
+ LiveCoding.Migrate.NoMigration: cellNoMigration :: (Typeable s, Functor m) => s -> (s -> a -> m (b, s)) -> Cell m a b
+ LiveCoding.Migrate.NoMigration: changes :: (Typeable a, Eq a, Monad m) => Cell m a (Maybe a)
+ LiveCoding.Migrate.NoMigration: data NoMigration a
+ LiveCoding.Migrate.NoMigration: dataTypeNoMigration :: DataType
+ LiveCoding.Migrate.NoMigration: delay :: (Monad m, Typeable a) => a -> Cell m a a
+ LiveCoding.Migrate.NoMigration: fromNoMigration :: a -> NoMigration a -> a
+ LiveCoding.Migrate.NoMigration: initializedConstr :: Constr
+ LiveCoding.Migrate.NoMigration: instance Data.Foldable.Foldable LiveCoding.Migrate.NoMigration.NoMigration
+ LiveCoding.Migrate.NoMigration: instance Data.Traversable.Traversable LiveCoding.Migrate.NoMigration.NoMigration
+ LiveCoding.Migrate.NoMigration: instance Data.Typeable.Internal.Typeable a => Data.Data.Data (LiveCoding.Migrate.NoMigration.NoMigration a)
+ LiveCoding.Migrate.NoMigration: instance GHC.Base.Functor LiveCoding.Migrate.NoMigration.NoMigration
+ LiveCoding.Migrate.NoMigration: instance GHC.Classes.Eq a => GHC.Classes.Eq (LiveCoding.Migrate.NoMigration.NoMigration a)
+ LiveCoding.Migrate.NoMigration: instance GHC.Show.Show a => GHC.Show.Show (LiveCoding.Migrate.NoMigration.NoMigration a)
+ LiveCoding.Migrate.NoMigration: uninitializedConstr :: Constr
- LiveCoding: (<<<) :: forall k cat (b :: k) (c :: k) (a :: k). Category cat => cat b c -> cat a b -> cat a c
+ LiveCoding: (<<<) :: forall {k} cat (b :: k) (c :: k) (a :: k). Category cat => cat b c -> cat a b -> cat a c
- LiveCoding: (>>>) :: forall k cat (a :: k) (b :: k) (c :: k). Category cat => cat a b -> cat b c -> cat a c
+ LiveCoding: (>>>) :: forall {k} cat (a :: k) (b :: k) (c :: k). Category cat => cat a b -> cat b c -> cat a c
- LiveCoding: [HRefl] :: forall k1 (a :: k1). a :~~: a
+ LiveCoding: [HRefl] :: forall {k1} (a :: k1). a :~~: a
- LiveCoding: [Refl] :: forall k (a :: k). a :~: a
+ LiveCoding: [Refl] :: forall {k} (a :: k). a :~: a
- LiveCoding: class Category a => Arrow (a :: Type -> Type -> Type)
+ LiveCoding: class Category a => Arrow (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type)
- LiveCoding: class Arrow a => ArrowApply (a :: Type -> Type -> Type)
+ LiveCoding: class Arrow a => ArrowApply (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type)
- LiveCoding: class Arrow a => ArrowChoice (a :: Type -> Type -> Type)
+ LiveCoding: class Arrow a => ArrowChoice (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type)
- LiveCoding: class Arrow a => ArrowLoop (a :: Type -> Type -> Type)
+ LiveCoding: class Arrow a => ArrowLoop (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type)
- LiveCoding: class ArrowZero a => ArrowPlus (a :: Type -> Type -> Type)
+ LiveCoding: class ArrowZero a => ArrowPlus (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type)
- LiveCoding: class Arrow a => ArrowZero (a :: Type -> Type -> Type)
+ LiveCoding: class Arrow a => ArrowZero (a :: TYPE LiftedRep -> TYPE LiftedRep -> Type)
- LiveCoding: data (a :: k1) :~~: (b :: k2)
+ LiveCoding: data (a :: k) :~: (b :: k)
- LiveCoding: eqT :: forall k (a :: k) (b :: k). (Typeable a, Typeable b) => Maybe (a :~: b)
+ LiveCoding: eqT :: forall {k} (a :: k) (b :: k). (Typeable a, Typeable b) => Maybe (a :~: b)
- LiveCoding: gcast :: forall k (a :: k) (b :: k) c. (Typeable a, Typeable b) => c a -> Maybe (c b)
+ LiveCoding: gcast :: forall {k} (a :: k) (b :: k) c. (Typeable a, Typeable b) => c a -> Maybe (c b)
- LiveCoding: gcast1 :: forall k1 k2 c (t :: k2 -> k1) (t' :: k2 -> k1) (a :: k2). (Typeable t, Typeable t') => c (t a) -> Maybe (c (t' a))
+ LiveCoding: gcast1 :: forall {k1} {k2} c (t :: k2 -> k1) (t' :: k2 -> k1) (a :: k2). (Typeable t, Typeable t') => c (t a) -> Maybe (c (t' a))
- LiveCoding: gcast2 :: forall k1 k2 k3 c (t :: k2 -> k3 -> k1) (t' :: k2 -> k3 -> k1) (a :: k2) (b :: k3). (Typeable t, Typeable t') => c (t a b) -> Maybe (c (t' a b))
+ LiveCoding: gcast2 :: forall {k1} {k2} {k3} c (t :: k2 -> k3 -> k1) (t' :: k2 -> k3 -> k1) (a :: k2) (b :: k3). (Typeable t, Typeable t') => c (t a b) -> Maybe (c (t' a b))
- LiveCoding: infix 4 :~~:
+ LiveCoding: infix 4 :~:
- LiveCoding: typeRep :: forall k proxy (a :: k). Typeable a => proxy a -> TypeRep
+ LiveCoding: typeRep :: forall {k} proxy (a :: k). Typeable a => proxy a -> TypeRep
- LiveCoding.GHCi: livelaunch :: Monad m => p -> m [Char]
+ LiveCoding.GHCi: livelaunch :: Monad m => p -> m String
- LiveCoding.GHCi: livestep :: Monad m => p -> m [Char]
+ LiveCoding.GHCi: livestep :: Monad m => p -> m String
- LiveCoding.GHCi: livestop :: Monad m => p -> m [Char]
+ LiveCoding.GHCi: livestop :: Monad m => p -> m String

Files

CHANGELOG.md view
@@ -1,6 +1,6 @@ # Revision history for essence-of-live-coding -## 0.2.6+## 0.2.7  * Add `changes` * Add support for GHC 9.0.2
Setup.hs view
@@ -1,2 +1,3 @@ import Distribution.Simple+ main = defaultMain
app/TestExceptions.hs view
@@ -9,8 +9,8 @@ -- essence-of-live-coding import LiveCoding -liveProgram = liveCell-  $ safely $ do+liveProgram = liveCell $+  safely $ do     try $ throwingCell     safe $ arr (const (3 :: Integer)) >>> sumC >>> arr (const ()) 
app/TestNonBlocking.hs view
@@ -1,9 +1,9 @@ {-# LANGUAGE Arrows #-} -module Main-  ( module Main-  , module X-  ) where+module Main (+  module Main,+  module X,+) where  -- base import Control.Arrow@@ -27,20 +27,21 @@   putStrLn "Push return to start a slow calculation."   runHandlingStateT $ foreground $ liveCell mainCell --- | Constantly count the number of ticks passed since program start.---   Whenever the keyboard return key is pressed,---   this number is printed, and passed into a slow "computation" in a separate thread,---   while the foreground thread is not blocked.---   When the background thread returns, the number is printed again.+{- | Constantly count the number of ticks passed since program start.+   Whenever the keyboard return key is pressed,+   this number is printed, and passed into a slow "computation" in a separate thread,+   while the foreground thread is not blocked.+   When the background thread returns, the number is printed again.+-} mainCell :: Cell (HandlingStateT IO) () () mainCell =   let keyboard = nonBlocking False $ constM getLine -- Only poll, never abort       mySlowId = nonBlocking True slowId -- Abort and restart when new data arrives-  in proc _ -> do-    n <- count                             -< ()-    lineMaybe <- keyboard                  -< Just ()-    let nString = show n <$ lineMaybe-    resampleMaybe (arrM $ lift . putStrLn) -< ("Calculating " ++) <$> nString-    resultMaybe <- mySlowId                -< nString-    resampleMaybe (arrM $ lift . putStrLn) -< ("Calculated "  ++) <$> resultMaybe-    arrM $ lift .threadDelay               -< 1000 -- Don't hog CPU+   in proc _ -> do+        n <- count -< ()+        lineMaybe <- keyboard -< Just ()+        let nString = show n <$ lineMaybe+        resampleMaybe (arrM $ lift . putStrLn) -< ("Calculating " ++) <$> nString+        resultMaybe <- mySlowId -< nString+        resampleMaybe (arrM $ lift . putStrLn) -< ("Calculated " ++) <$> resultMaybe+        arrM $ lift . threadDelay -< 1000 -- Don't hog CPU
essence-of-live-coding.cabal view
@@ -1,5 +1,5 @@ name:                essence-of-live-coding-version:             0.2.6+version:             0.2.7 synopsis: General purpose live coding framework description:   essence-of-live-coding is a general purpose and type safe live coding framework.@@ -25,12 +25,12 @@  source-repository head   type:     git-  location: git@github.com:turion/essence-of-live-coding.git+  location: https://github.com/turion/essence-of-live-coding.git  source-repository this   type:     git-  location: git@github.com:turion/essence-of-live-coding.git-  tag:      v0.2.6+  location: https://github.com/turion/essence-of-live-coding.git+  tag:      v0.2.7   library@@ -64,6 +64,7 @@     , LiveCoding.Migrate     , LiveCoding.Migrate.Cell     , LiveCoding.Migrate.Migration+    , LiveCoding.Migrate.NoMigration     , LiveCoding.Migrate.Monad.Trans     , LiveCoding.Migrate.Debugger     , LiveCoding.RuntimeIO@@ -98,6 +99,7 @@   main-is: Main.hs   other-modules:       Cell+    , Migrate.NoMigration     , Cell.Monad.Trans     , Cell.Util     , Feedback
src/LiveCoding.hs view
@@ -1,6 +1,5 @@-module LiveCoding-  (module X)-  where+module LiveCoding (module X)+where  -- base import Control.Arrow as X hiding (app)@@ -24,21 +23,22 @@ import LiveCoding.Exceptions.Finite as X import LiveCoding.Forever as X import LiveCoding.Handle as X-import LiveCoding.HandlingState as X-    ( HandlingStateT,-      HandlingState(..),-      Handling(..),-      isRegistered,-      runHandlingStateT,-      runHandlingStateC,-      runHandlingState,-    ) import LiveCoding.Handle.Examples as X+import LiveCoding.HandlingState as X (+  Handling (..),+  HandlingState (..),+  HandlingStateT,+  isRegistered,+  runHandlingState,+  runHandlingStateC,+  runHandlingStateT,+ ) import LiveCoding.LiveProgram as X import LiveCoding.LiveProgram.HotCodeSwap as X import LiveCoding.LiveProgram.Monad.Trans as X import LiveCoding.Migrate as X import LiveCoding.Migrate.Debugger as X import LiveCoding.Migrate.Migration as X+import LiveCoding.Migrate.NoMigration as X hiding (changes, delay) import LiveCoding.RuntimeIO as X hiding (update) import LiveCoding.RuntimeIO.Launch as X hiding (foreground)
src/LiveCoding/Cell/HotCodeSwap.hs view
@@ -4,14 +4,14 @@ import LiveCoding.Cell import LiveCoding.Migrate -hotCodeSwapCell-  :: Cell m a b-  -> Cell m a b-  -> Cell m a b+hotCodeSwapCell ::+  Cell m a b ->+  Cell m a b ->+  Cell m a b hotCodeSwapCell   (Cell newState newStep)-  (Cell oldState _)-  = Cell-  { cellState = migrate newState oldState-  , cellStep  = newStep-  }+  (Cell oldState _) =+    Cell+      { cellState = migrate newState oldState+      , cellStep = newStep+      }
src/LiveCoding/Cell/Monad.hs view
@@ -1,58 +1,66 @@ {-# LANGUAGE QuantifiedConstraints #-}-{-# LANGUAGE TupleSections #-}-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+ {- | Handling monad morphisms. -} module LiveCoding.Cell.Monad where  -- essence-of-live-coding-import LiveCoding.Cell-import Control.Arrow ((>>>), Arrow(arr))++import Control.Arrow (Arrow (arr), (>>>)) import Data.Data (Data)+import LiveCoding.Cell  -- | Apply a monad morphism that also transforms the output to a cell.-hoistCellOutput-  :: (Monad m1, Monad m2)-  => (forall s . m1 (b1, s) -> m2 (b2, s))-  -> Cell m1 a b1-  -> Cell m2 a b2+hoistCellOutput ::+  (Monad m1, Monad m2) =>+  (forall s. m1 (b1, s) -> m2 (b2, s)) ->+  Cell m1 a b1 ->+  Cell m2 a b2 hoistCellOutput morph = hoistCellKleisli_ (morph .)  -- | Apply a transformation of Kleisli morphisms to a cell.-hoistCellKleisli_-  :: (Monad m1, Monad m2)-  => (forall s . (a1 -> m1 (b1, s)) -> (a2 -> m2 (b2, s)))-  -> Cell m1 a1 b1-  -> Cell m2 a2 b2+hoistCellKleisli_ ::+  (Monad m1, Monad m2) =>+  (forall s. (a1 -> m1 (b1, s)) -> (a2 -> m2 (b2, s))) ->+  Cell m1 a1 b1 ->+  Cell m2 a2 b2 hoistCellKleisli_ morph = hoistCellKleisli (morph .)  -- | Apply a transformation of stateful Kleisli morphisms to a cell.-hoistCellKleisli-  :: (Monad m1, Monad m2)-  => (forall s . (s -> a1 -> m1 (b1, s)) -> (s -> a2 -> m2 (b2, s)))-  -> Cell m1 a1 b1-  -> Cell m2 a2 b2-hoistCellKleisli morph ArrM { .. } = ArrM-  { runArrM = (fmap fst .) $ ($ ()) $ morph $ const $ runArrM >>> fmap ( , ())-  }-hoistCellKleisli morph Cell { .. } = Cell-  { cellStep = morph cellStep-  , ..-  }+hoistCellKleisli ::+  (Monad m1, Monad m2) =>+  (forall s. (s -> a1 -> m1 (b1, s)) -> (s -> a2 -> m2 (b2, s))) ->+  Cell m1 a1 b1 ->+  Cell m2 a2 b2+hoistCellKleisli morph ArrM {..} =+  ArrM+    { runArrM = (fmap fst .) $ ($ ()) $ morph $ const $ runArrM >>> fmap (,())+    }+hoistCellKleisli morph Cell {..} =+  Cell+    { cellStep = morph cellStep+    , ..+    } --- | Apply a transformation of stateful Kleisli morphisms to a cell,---   changing the state type.-hoistCellKleisliStateChange-  :: (Monad m1, Monad m2, (forall s . Data s => Data (t s)))-  => (forall s . (  s -> a1 -> m1 (b1,   s))-              -> (t s -> a2 -> m2 (b2, t s)))-  -> (forall s . (s -> t s))-  -> Cell m1 a1 b1-  -> Cell m2 a2 b2-hoistCellKleisliStateChange morph init Cell { .. } = Cell-  { cellStep  = morph cellStep-  , cellState = init cellState-  }+{- | Apply a transformation of stateful Kleisli morphisms to a cell,+   changing the state type.+-}+hoistCellKleisliStateChange ::+  (Monad m1, Monad m2, (forall s. Data s => Data (t s))) =>+  ( forall s.+    (s -> a1 -> m1 (b1, s)) ->+    (t s -> a2 -> m2 (b2, t s))+  ) ->+  (forall s. (s -> t s)) ->+  Cell m1 a1 b1 ->+  Cell m2 a2 b2+hoistCellKleisliStateChange morph init Cell {..} =+  Cell+    { cellStep = morph cellStep+    , cellState = init cellState+    } hoistCellKleisliStateChange morph init cell = hoistCellKleisliStateChange morph init $ toCell cell
src/LiveCoding/Cell/Monad/Trans.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RecordWildCards #-}+ {- | Handling monad transformers. -}@@ -10,8 +11,8 @@ import Data.Data (Data)  -- transformers-import Control.Monad.Trans.Reader (runReaderT, ReaderT)-import Control.Monad.Trans.State.Strict (StateT (..), runStateT, evalStateT)+import Control.Monad.Trans.Reader (ReaderT, runReaderT)+import Control.Monad.Trans.State.Strict (StateT (..), evalStateT, runStateT) import Control.Monad.Trans.Writer.Strict  -- essence-of-live-coding@@ -19,29 +20,29 @@ import LiveCoding.Cell.Monad  -- | Push effectful state into the internal state of a cell-runStateC-  :: (Data stateT, Monad m)-  => Cell (StateT stateT m) a  b-  -- ^ A cell with a state effect-  -> stateT-  -- ^ The initial state-  -> Cell                m  a (b, stateT)-  -- ^ The cell, returning its current state+runStateC ::+  (Data stateT, Monad m) =>+  -- | A cell with a state effect+  Cell (StateT stateT m) a b ->+  -- | The initial state+  stateT ->+  -- | The cell, returning its current state+  Cell m a (b, stateT) runStateC cell stateT = hoistCellKleisliStateChange morph init cell   where-    morph step State { .. } a = do+    morph step State {..} a = do       ((b, stateInternal), stateT) <- runStateT (step stateInternal a) stateT-      return ((b, stateT), State { .. })-    init stateInternal = State { .. }+      return ((b, stateT), State {..})+    init stateInternal = State {..}  -- | Like 'runStateC', but does not return the current state.-runStateC_-  :: (Data stateT, Monad m)-  => Cell (StateT stateT m) a b-  -- ^ A cell with a state effect-  -> stateT-  -- ^ The initial state-  -> Cell                m  a b+runStateC_ ::+  (Data stateT, Monad m) =>+  -- | A cell with a state effect+  Cell (StateT stateT m) a b ->+  -- | The initial state+  stateT ->+  Cell m a b runStateC_ cell stateT = runStateC cell stateT >>> arr fst  -- | The internal state of a cell to which 'runStateC' or 'runStateL' has been applied.@@ -52,21 +53,22 @@   deriving (Data, Eq, Show)  -- | Supply a 'ReaderT' environment before running the cell-runReaderC-  ::               r-  -> Cell (ReaderT r m) a b-  -> Cell            m  a b+runReaderC ::+  r ->+  Cell (ReaderT r m) a b ->+  Cell m a b runReaderC r = hoistCell $ flip runReaderT r  -- | Supply a 'ReaderT' environment live-runReaderC'-  :: Monad m-  => Cell (ReaderT r m) a b-  -> Cell m (r, a) b+runReaderC' ::+  Monad m =>+  Cell (ReaderT r m) a b ->+  Cell m (r, a) b runReaderC' = hoistCellKleisli_ $ \action (r, a) -> runReaderT (action a) r --- | Run the effects of the 'WriterT' monad,---   collecting all its output in the second element of the tuple.+{- | Run the effects of the 'WriterT' monad,+   collecting all its output in the second element of the tuple.+-} runWriterC :: (Monoid w, Monad m) => Cell (WriterT w m) a b -> Cell m a (w, b) runWriterC = hoistCellOutput $ fmap reorder . runWriterT   where
src/LiveCoding/Cell/NonBlocking.hs view
@@ -1,14 +1,14 @@ {-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-} -module LiveCoding.Cell.NonBlocking-  ( nonBlocking-  )-  where+module LiveCoding.Cell.NonBlocking (+  nonBlocking,+)+where  -- base import Control.Concurrent-import Control.Monad ((>=>), void, when)+import Control.Monad (void, when, (>=>)) import Data.Data  -- essence-of-live-coding@@ -18,10 +18,11 @@ import LiveCoding.HandlingState  threadVarHandle :: Handle IO (MVar ThreadId)-threadVarHandle = Handle-  { create = newEmptyMVar-  , destroy = tryTakeMVar >=> mapM_ killThread-  }+threadVarHandle =+  Handle+    { create = newEmptyMVar+    , destroy = tryTakeMVar >=> mapM_ killThread+    }  {- | Wrap a cell in a non-blocking way. Every incoming sample of @nonBlocking cell@ results in an immediate output,@@ -30,38 +31,39 @@ The resulting cell can be polled by sending 'Nothing'. The boolean flag controls whether the current computation is aborted and restarted when new data arrives. -}-nonBlocking-  :: Typeable b-  => Bool-  -- ^ Pass 'True' to abort the computation when new data arrives. 'False' discards new data.-  -> Cell IO a b-  -> Cell (HandlingStateT IO) (Maybe a) (Maybe b)-nonBlocking abort Cell { .. } = proc aMaybe -> do-  threadVar <- handling threadVarHandle            -< ()-  resultVar <- handling emptyMVarHandle            -< ()-  liftCell Cell { cellStep = nonBlockingStep, .. } -< (aMaybe, threadVar, resultVar)-    where-      nonBlockingStep s (Nothing, threadVar, resultVar) = do-        bsMaybe <- tryTakeMVar resultVar-        case bsMaybe of-          Just (b, s') -> do-            threadId <- takeMVar threadVar-            killThread threadId-            return (Just b, s')-          Nothing -> return (Nothing, s)-      nonBlockingStep s (Just a, threadVar, resultVar) = do-        noThreadRunning <- if abort-            -- Abort the current computation if it is still running-          then do+nonBlocking ::+  Typeable b =>+  -- | Pass 'True' to abort the computation when new data arrives. 'False' discards new data.+  Bool ->+  Cell IO a b ->+  Cell (HandlingStateT IO) (Maybe a) (Maybe b)+nonBlocking abort Cell {..} = proc aMaybe -> do+  threadVar <- handling threadVarHandle -< ()+  resultVar <- handling emptyMVarHandle -< ()+  liftCell Cell {cellStep = nonBlockingStep, ..} -< (aMaybe, threadVar, resultVar)+  where+    nonBlockingStep s (Nothing, threadVar, resultVar) = do+      bsMaybe <- tryTakeMVar resultVar+      case bsMaybe of+        Just (b, s') -> do+          threadId <- takeMVar threadVar+          killThread threadId+          return (Just b, s')+        Nothing -> return (Nothing, s)+    nonBlockingStep s (Just a, threadVar, resultVar) = do+      noThreadRunning <-+        if abort+          then -- Abort the current computation if it is still running+          do             maybeThreadId <- tryTakeMVar threadVar             mapM_ killThread maybeThreadId             return True-          -- No computation currently running-          else isEmptyMVar threadVar-        when noThreadRunning $ do-          threadId <- forkIO $ putMVar resultVar =<< cellStep s a-          putMVar threadVar threadId-        nonBlockingStep s (Nothing, threadVar, resultVar)+          else -- No computation currently running+            isEmptyMVar threadVar+      when noThreadRunning $ do+        threadId <- forkIO $ putMVar resultVar =<< cellStep s a+        putMVar threadVar threadId+      nonBlockingStep s (Nothing, threadVar, resultVar)  -- It would have been nice to refactor this with 'hoistCellKleisli', -- but that would expose the existential state type to the handle.
src/LiveCoding/Cell/Resample.hs view
@@ -1,13 +1,13 @@+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+ {- | Run a cell at a fixed integer multiple speed. The general approach is to take an existing cell (the "inner" cell) and produce a new cell (the "outer" cell) that will accept several copies of the input. The inner cell is stepped for each input. -}--{-# LANGUAGE BangPatterns #-}-{-# LANGUAGE NamedFieldPuns #-}-{-# LANGUAGE RecordWildCards #-} module LiveCoding.Cell.Resample where  -- base@@ -16,7 +16,7 @@ import GHC.TypeNats  -- vector-sized-import Data.Vector.Sized+import Data.Vector.Sized (Vector, fromList, toList)  -- essence-of-live-coding import LiveCoding.Cell@@ -32,9 +32,24 @@   where     morph _ s [] = return ([], s)     morph singleStep s (a : as) = do-      (!b , s' ) <- singleStep s a+      (!b, s') <- singleStep s a       (!bs, s'') <- morph singleStep s' as       return (b : bs, s'')  resampleMaybe :: Monad m => Cell m a b -> Cell m (Maybe a) (Maybe b) resampleMaybe cell = arr maybeToList >>> resampleList cell >>> arr listToMaybe++{- | Create as many cells as the input list is long and execute them in parallel+ (in the sense that each one has a separate state). At each tick the list with+ the different states grows or shrinks depending on the size of the input list.++ Similar to Yampa's [parC](https://hackage.haskell.org/package/Yampa-0.13.3/docs/FRP-Yampa-Switches.html#v:parC).+-}+resampleListPar :: Monad m => Cell m a b -> Cell m [a] [b]+resampleListPar (Cell initial step) = Cell {..}+  where+    cellState = []+    cellStep s xs = unzip <$> traverse (uncurry step) (zip s' xs)+      where+        s' = s ++ replicate (length xs - length s) initial+resampleListPar (ArrM f) = ArrM (traverse f)
src/LiveCoding/Cell/Util.hs view
@@ -1,11 +1,12 @@ {-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-}+ module LiveCoding.Cell.Util where  -- base import Control.Arrow-import Control.Monad (join, guard)+import Control.Monad (guard, join) import Control.Monad.IO.Class import Data.Data (Data) import Data.Foldable (toList)@@ -35,49 +36,53 @@ count :: Monad m => Cell m a Integer count = arr (const 1) >>> sumC --- | Accumulate all incoming data,---   using the given fold function and start value.---   For example, if @'foldC' f b@ receives inputs @a0@, @a1@,...---   it will output @b@, @f a0 b@, @f a1 $ f a0 b@, and so on.+{- | Accumulate all incoming data,+   using the given fold function and start value.+   For example, if @'foldC' f b@ receives inputs @a0@, @a1@,...+   it will output @b@, @f a0 b@, @f a1 $ f a0 b@, and so on.+-} foldC :: (Data b, Monad m) => (a -> b -> b) -> b -> Cell m a b-foldC step cellState = Cell { .. }+foldC step cellState = Cell {..}   where     cellStep b a = let b' = step a b in return (b, b')  -- | Like 'foldC', but does not delay the output. foldC' :: (Data b, Monad m) => (a -> b -> b) -> b -> Cell m a b-foldC' step cellState = Cell { .. }+foldC' step cellState = Cell {..}   where     cellStep b a = let b' = step a b in return (b', b') --- | Initialise with a value 'a'.---   If the input is 'Nothing', @'hold' a@ will output the stored indefinitely.---   A new value can be stored by inputting @'Just' a@.+{- | Initialise with a value 'a'.+   If the input is 'Nothing', @'hold' a@ will output the stored indefinitely.+   A new value can be stored by inputting @'Just' a@.+-} hold :: (Data a, Monad m) => a -> Cell m (Maybe a) a hold a = feedback a $ proc (ma, aOld) -> do   let aNew = fromMaybe aOld ma   returnA -< (aNew, aNew) --- | Outputs @'Just' a@ whenever the the value a changes and 'Nothing' otherwise.---  The first output is always 'Nothing'. The following holds:------  @---    delay a >>> changes >>> hold a == delay a---  @-changes-  :: (Data a, Eq a, Monad m) -  => Cell m a (Maybe a)+{- | Outputs @'Just' a@ whenever the the value a changes and 'Nothing' otherwise.+  The first output is always 'Nothing'. The following holds:++  @+    delay a >>> changes >>> hold a == delay a+  @+-}+changes ::+  (Data a, Eq a, Monad m) =>+  Cell m a (Maybe a) changes = proc a -> do   aLast <- delay Nothing -< Just a-  returnA -< do+  returnA+    -< do       aLast' <- aLast       guard $ a /= aLast'       return a  -- | Like 'hold', but returns 'Nothing' until it is initialised by a @'Just' a@ value.-holdJust-  :: (Monad m, Data a)-  => Cell m (Maybe a) (Maybe a)+holdJust ::+  (Monad m, Data a) =>+  Cell m (Maybe a) (Maybe a) holdJust = feedback Nothing $ arr keep   where     keep (Nothing, Nothing) = (Nothing, Nothing)@@ -86,7 +91,7 @@  -- | Hold the first value and output it indefinitely. holdFirst :: (Data a, Monad m) => Cell m a a-holdFirst = Cell { .. }+holdFirst = Cell {..}   where     cellState = Nothing     cellStep Nothing x = return (x, Just x)@@ -96,20 +101,23 @@ boundedFIFO :: (Data a, Monad m) => Int -> Cell m (Maybe a) (Seq a) boundedFIFO n = foldC' step empty   where-    step Nothing  as = as+    step Nothing as = as     step (Just a) as = Sequence.take n $ a <| as --- | Buffers and returns the elements in First-In-First-Out order,---   returning 'Nothing' whenever the buffer is empty.+{- | Buffers and returns the elements in First-In-First-Out order,+   returning 'Nothing' whenever the buffer is empty.+-} fifo :: (Monad m, Data a) => Cell m (Seq a) (Maybe a) fifo = feedback empty $ proc (as, accum) -> do   let accum' = accum >< as-  returnA -< case accum' of-    Empty    -> (Nothing, empty)-    a :<| as -> (Just a , as)+  returnA+    -< case accum' of+      Empty -> (Nothing, empty)+      a :<| as -> (Just a, as) --- | Like 'fifo', but accepts lists as input.---   Each step is O(n) in the length of the list.+{- | Like 'fifo', but accepts lists as input.+   Each step is O(n) in the length of the list.+-} fifoList :: (Monad m, Data a) => Cell m [a] (Maybe a) fifoList = arr fromList >>> fifo @@ -137,10 +145,10 @@  -- | A command to send to 'buffer'. data BufferCommand a-  = Push a-  -- ^ Add an 'a' to the buffer.-  | Pop-  -- ^ Remove the oldest element from the buffer.+  = -- | Add an 'a' to the buffer.+    Push a+  | -- | Remove the oldest element from the buffer.+    Pop  -- | Pushes @'Just' a@ and does nothing on 'Nothing'. maybePush :: Maybe a -> [BufferCommand a]@@ -159,12 +167,12 @@ * Remove elements by inputting 'Pop'. -} buffer :: (Monad m, Data a) => Cell m [BufferCommand a] (Maybe a)-buffer = Cell { .. }+buffer = Cell {..}   where     cellState = empty     cellStep as commands = return (currentHead as, nextBuffer as commands)     currentHead as = case viewl as of-      EmptyL   -> Nothing+      EmptyL -> Nothing       a :< as' -> Just a     nextBuffer as [] = as     nextBuffer as (Push a : commands) = nextBuffer (as |> a) commands@@ -181,14 +189,14 @@  This construction guarantees that @cell@ produces exactly one output for every input value. -}-buffered-  :: (Monad m, Data a)-  => Cell m (Maybe a) (Maybe b)-  -> Cell m (Maybe a) (Maybe b)+buffered ::+  (Monad m, Data a) =>+  Cell m (Maybe a) (Maybe b) ->+  Cell m (Maybe a) (Maybe b) buffered cell = feedback Nothing $ proc (aMaybe, ticked) -> do   aMaybe' <- buffer -< maybePop ticked ++ maybePush aMaybe-  bMaybe' <- cell   -< aMaybe'-  returnA           -< (bMaybe', void bMaybe')+  bMaybe' <- cell -< aMaybe'+  returnA -< (bMaybe', void bMaybe')  -- * Detecting change @@ -198,22 +206,25 @@ For this functionality, see "LiveCoding.Handle". Also, when moving such a cell, the action may not be triggered reliably. -}-onChange-  :: (Monad m, Data p, Eq p)-  => p -- ^ This parameter has to change during live coding to trigger an action-  -> (p -> p -> a -> m b) -- ^ This action gets passed the old parameter and the new parameter-  -> Cell m a (Maybe b)+onChange ::+  (Monad m, Data p, Eq p) =>+  -- | This parameter has to change during live coding to trigger an action+  p ->+  -- | This action gets passed the old parameter and the new parameter+  (p -> p -> a -> m b) ->+  Cell m a (Maybe b) onChange p action = proc a -> do   pCurrent <- arr $ const p -< ()   pPrevious <- delay p -< pCurrent   arrM $ whenDifferent action -< (pCurrent, pPrevious, a)  -- | Like 'onChange'', but with a dynamic input.-onChange'-  :: (Monad m, Data p, Eq p)-  => (p -> p -> a -> m b) -- ^ This action gets passed the old parameter and the new parameter-  -> Cell m (p, a) (Maybe b)+onChange' ::+  (Monad m, Data p, Eq p) =>+  -- | This action gets passed the old parameter and the new parameter+  (p -> p -> a -> m b) ->+  Cell m (p, a) (Maybe b) onChange' action = proc (pCurrent, a) -> do   pPrevious <- delay Nothing -< Just pCurrent-  bMaybeMaybe <- resampleMaybe $ arrM $ whenDifferent action -< ( , pCurrent, a) <$> pPrevious+  bMaybeMaybe <- resampleMaybe $ arrM $ whenDifferent action -< (,pCurrent,a) <$> pPrevious   returnA -< join bMaybeMaybe
src/LiveCoding/Cell/Util/Internal.hs view
@@ -4,4 +4,4 @@ whenDifferent :: (Eq p, Monad m) => (p -> p -> a -> m b) -> (p, p, a) -> m (Maybe b) whenDifferent action (pOld, pNew, a)   | pOld == pNew = Just <$> action pOld pNew a-  | otherwise    = return Nothing+  | otherwise = return Nothing
src/LiveCoding/Debugger/StatePrint.hs view
@@ -8,7 +8,7 @@  -- base import Data.Data-import Data.Maybe (fromMaybe, fromJust)+import Data.Maybe (fromJust, fromMaybe) import Data.Proxy import Data.Typeable import Unsafe.Coerce@@ -25,8 +25,8 @@ import LiveCoding.Cell import LiveCoding.Cell.Feedback import LiveCoding.Debugger-import LiveCoding.Forever import LiveCoding.Exceptions+import LiveCoding.Forever  statePrint :: Debugger IO statePrint = Debugger $ liveCell $ arrM $ const $ do@@ -34,22 +34,24 @@   lift $ putStrLn $ stateShow s  stateShow :: Data s => s -> String-stateShow-  =       gshow-  `ext2Q` compositionShow-  `ext2Q` foreverEShow-  `ext2Q` feedbackShow-  `ext2Q` parallelShow-  `ext2Q` exceptShow-  `ext2Q` choiceShow+stateShow =+  gshow+    `ext2Q` compositionShow+    `ext2Q` foreverEShow+    `ext2Q` feedbackShow+    `ext2Q` parallelShow+    `ext2Q` exceptShow+    `ext2Q` choiceShow  isUnit :: Data s => s -> Bool-isUnit = mkQ False-          (\() -> True)-  `ext2Q` (\(a, b) -> isUnit a && isUnit b)-  `ext2Q` (\(Composition s1 s2) -> isUnit s1 && isUnit s2)-  `ext2Q` (\(Parallel s1 s2) -> isUnit s1 && isUnit s2)-  `ext2Q` (\(Choice sL sR) -> isUnit sL && isUnit sR)+isUnit =+  mkQ+    False+    (\() -> True)+    `ext2Q` (\(a, b) -> isUnit a && isUnit b)+    `ext2Q` (\(Composition s1 s2) -> isUnit s1 && isUnit s2)+    `ext2Q` (\(Parallel s1 s2) -> isUnit s1 && isUnit s2)+    `ext2Q` (\(Choice sL sR) -> isUnit sL && isUnit sR)  compositionShow :: (Data s1, Data s2) => Composition s1 s2 -> String compositionShow (Composition s1 s2)@@ -65,26 +67,28 @@   | otherwise = "(" ++ stateShow s1 ++ " *** " ++ stateShow s2 ++ ")"  foreverEShow :: (Data e, Data s) => ForeverE e s -> String-foreverEShow ForeverE { .. }-  =  "forever("-  ++ (if isUnit lastException then "" else gshow lastException ++ ", ")-  ++ stateShow initState ++ "): " ++ stateShow currentState+foreverEShow ForeverE {..} =+  "forever("+    ++ (if isUnit lastException then "" else gshow lastException ++ ", ")+    ++ stateShow initState+    ++ "): "+    ++ stateShow currentState  feedbackShow :: (Data state, Data s) => Feedback state s -> String-feedbackShow Feedback { .. } = "feedback " ++ gshow sAdditional ++ " $ " ++ stateShow sPrevious+feedbackShow Feedback {..} = "feedback " ++ gshow sAdditional ++ " $ " ++ stateShow sPrevious  exceptShow :: (Data s, Data e) => ExceptState s e -> String exceptShow (NotThrown s) = "NotThrown: " ++ stateShow s ++ "\n"-exceptShow (Exception e)-  =  "Exception"-  ++ (if isUnit e then "" else " " ++ gshow e)-  ++ ":\n"+exceptShow (Exception e) =+  "Exception"+    ++ (if isUnit e then "" else " " ++ gshow e)+    ++ ":\n"  choiceShow :: (Data stateL, Data stateR) => Choice stateL stateR -> String-choiceShow Choice { .. }-  | isUnit choiceLeft  = "+" ++ stateShow choiceRight ++ "+"-  | isUnit choiceRight = "+" ++ stateShow choiceLeft  ++ "+"-  | otherwise     = "+" ++ stateShow choiceLeft ++ " +++ " ++ stateShow choiceRight ++ "+"+choiceShow Choice {..}+  | isUnit choiceLeft = "+" ++ stateShow choiceRight ++ "+"+  | isUnit choiceRight = "+" ++ stateShow choiceLeft ++ "+"+  | otherwise = "+" ++ stateShow choiceLeft ++ " +++ " ++ stateShow choiceRight ++ "+"  {- -- TODO  Leave out for now from the examples and open bug when public@@ -98,9 +102,11 @@        => c (t a b) -> Maybe (c (t' a b)) gcast2 x = fmap (\Refl -> x) (eqT :: Maybe (t :~: t')) -}-gcast3-  :: forall f t t' a b c. (Typeable t, Typeable t')-  => f (t a b c) -> Maybe (f (t' a b c))+gcast3 ::+  forall f t t' a b c.+  (Typeable t, Typeable t') =>+  f (t a b c) ->+  Maybe (f (t' a b c)) gcast3 x = fmap (\Refl -> x) (eqT :: Maybe (t :~: t'))  -- from https://stackoverflow.com/questions/14447050/how-to-define-syb-functions-for-type-extension-for-tertiary-type-constructors-e?rq=1@@ -119,8 +125,8 @@ dropMaybe _ = id -} ---thing :: (Typeable t) => (forall b c d . (Data b, Data c, Data d) => f (t b c d)) -> TypeRep---thing = typeRep+-- thing :: (Typeable t) => (forall b c d . (Data b, Data c, Data d) => f (t b c d)) -> TypeRep+-- thing = typeRep {- dataCast3   :: (Typeable t, Data a)@@ -144,19 +150,19 @@ --ext3 def ext = fromMaybe def $ gcast3' ext --ext3 def ext = maybe def id $ dataCast3 ext -}-ext3-  :: (Data a, Data b, Data c, Data d, Typeable t, Typeable f)-  => f a-  -> f (t b c d)-  -> f a+ext3 ::+  (Data a, Data b, Data c, Data d, Typeable t, Typeable f) =>+  f a ->+  f (t b c d) ->+  f a ext3 def ext = maybe def id $ cast ext -ext3Q-  :: (Data a, Data b, Data c, Data d, Typeable t, Typeable q)-  => (a -> q)-  -> (t b c d -> q)-  -> a -> q+ext3Q ::+  (Data a, Data b, Data c, Data d, Typeable t, Typeable q) =>+  (a -> q) ->+  (t b c d -> q) ->+  a ->+  q ext3Q def ext = unQ ((Q def) `ext3` (Q ext)) --newtype Q q x = Q { unQ :: x -> q }+newtype Q q x = Q {unQ :: x -> q}
src/LiveCoding/External.hs view
@@ -1,10 +1,10 @@+{-# 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). -}--{-# LANGUAGE Arrows #-}-{-# LANGUAGE RecordWildCards #-} module LiveCoding.External where  -- base@@ -27,14 +27,14 @@  concurrently :: (MonadIO m, Monoid eOut) => ExternalCell m eIn eOut a b -> IO (Cell m a b, ExternalLoop eIn eOut) concurrently externalCell = do-  inVar  <- newEmptyMVar+  inVar <- newEmptyMVar   outVar <- newEmptyMVar   let     cell = proc a -> do-      eIn       <- constM (liftIO $ takeMVar inVar)      -< ()+      eIn <- constM (liftIO $ takeMVar inVar) -< ()       (eOut, b) <- runWriterC (runReaderC' externalCell) -< (eIn, a)-      arrM (liftIO . putMVar outVar)                     -< eOut-      returnA                                            -< b+      arrM (liftIO . putMVar outVar) -< eOut+      returnA -< b     externalLoop = arrM (putMVar inVar) >>> constM (takeMVar outVar)   return (cell, externalLoop) 
src/LiveCoding/GHCi.hs view
@@ -1,7 +1,8 @@ {-# LANGUAGE ExistentialQuantification #-}-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}+ {- | Support functions to call common live coding functionalities like launching and reloading from a @ghci@ or @cabal repl@ session. @@ -14,8 +15,8 @@  -- base import Control.Concurrent-import Control.Exception (SomeException, try, Exception (toException, displayException))-import Control.Monad (void, (>=>), join)+import Control.Exception (Exception (displayException, toException), SomeException, try)+import Control.Monad (join, void, (>=>)) import Data.Data import Data.Function ((&)) @@ -34,25 +35,27 @@  -- | An exception type marking the absence of a foreign store of the correct type. data NoStore = NoStore-  deriving Show+  deriving (Show)  instance Exception NoStore  -- * Retrieving launched programs from the foreign store --- | Try to retrieve a 'LiveProgram' of a given type from the 'Store',---   handling all 'IO' exceptions.---   Returns 'Right Nothing' if the store didn't exist.-possiblyLaunchedProgram-  :: Launchable m-  => Proxy m-  -> IO (Either SomeException (LaunchedProgram m))+{- | Try to retrieve a 'LiveProgram' of a given type from the 'Store',+   handling all 'IO' exceptions.+   Returns 'Right Nothing' if the store didn't exist.+-}+possiblyLaunchedProgram ::+  Launchable m =>+  Proxy m ->+  IO (Either SomeException (LaunchedProgram m)) possiblyLaunchedProgram _ = do   storeMaybe <- lookupStore 0   fmap join $ try $ traverse readStore $ maybe (Left $ toException NoStore) Right storeMaybe --- | Try to load a 'LiveProgram' of a given type from the 'Store'.---   If the store doesn't contain a program, it is (re)started.+{- | Try to load a 'LiveProgram' of a given type from the 'Store'.+   If the store doesn't contain a program, it is (re)started.+-} sync :: Launchable m => LiveProgram m -> IO () sync program = do   launchedProgramPossibly <- possiblyLaunchedProgram $ proxyFromLiveProgram program@@ -75,12 +78,13 @@ save :: Launchable m => LaunchedProgram m -> IO () save = writeStore $ Store 0 --- | Try to retrieve a 'LaunchedProgram' from the 'Store',---   and if successful, stop it.-stopStored-  :: Launchable m-  => Proxy m-  -> IO ()+{- | Try to retrieve a 'LaunchedProgram' from the 'Store',+   and if successful, stop it.+-}+stopStored ::+  Launchable m =>+  Proxy m ->+  IO () stopStored proxy = do   launchedProgramPossibly <- possiblyLaunchedProgram proxy   either (putStrLn . displayException) stop launchedProgramPossibly@@ -88,15 +92,19 @@ -- * GHCi commands  -- ** Debugging+ -- TODO Could also parametrise this and all other commands by the 'liveProgram' --- | Initialise a launched program in the store,---   but don't start it.-liveinit _ = return $ unlines-  [ "programVar <- newMVar liveProgram"-  , "threadId <- myThreadId"-  , "save LaunchedProgram { .. }"-  ]+{- | Initialise a launched program in the store,+   but don't start it.+-}+liveinit _ =+  return $+    unlines+      [ "programVar <- newMVar liveProgram"+      , "threadId <- myThreadId"+      , "save LaunchedProgram { .. }"+      ]  -- | Run one program step, assuming you have a launched program in a variable @launchedProgram@. livestep _ = return "stepLaunchedProgram launchedProgram"@@ -107,10 +115,12 @@ livelaunch _ = return "sync liveProgram"  -- | Reload the code and do hot code swap and migration.-livereload _ = return $ unlines-  [ ":reload"-  , "sync liveProgram"-  ]+livereload _ =+  return $+    unlines+      [ ":reload"+      , "sync liveProgram"+      ]  -- | Stop the program. livestop _ = return "stopStored $ proxyFromLiveProgram liveProgram"
src/LiveCoding/Handle.hs view
@@ -11,7 +11,7 @@ import Data.Data  -- transformers-import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.Monad.Trans.Class (MonadTrans (lift))  -- mmorph import Control.Monad.Morph@@ -19,6 +19,7 @@ -- essence-of-live-coding import LiveCoding.Cell import LiveCoding.HandlingState+import LiveCoding.Migrate.NoMigration  {- | Container for unserialisable values, such as 'IORef's, threads, 'MVar's, pointers, and device handles.@@ -41,10 +42,11 @@   }  instance MFunctor Handle where-  hoist morphism Handle { .. } = Handle-    { create = morphism create-    , destroy = morphism . destroy-    }+  hoist morphism Handle {..} =+    Handle+      { create = morphism create+      , destroy = morphism . destroy+      }  {- | Combine two handles to one. @@ -56,10 +58,11 @@ (because the destructor is contravariant in @h@). -} combineHandles :: Applicative m => Handle m h1 -> Handle m h2 -> Handle m (h1, h2)-combineHandles handle1 handle2 = Handle-  { create = ( , ) <$> create handle1 <*> create handle2-  , destroy = \(h1, h2) -> destroy handle2 h2 *> destroy handle1 h1-  }+combineHandles handle1 handle2 =+  Handle+    { create = (,) <$> create handle1 <*> create handle2+    , destroy = \(h1, h2) -> destroy handle2 h2 *> destroy handle1 h1+    }  {- | Hide a handle in a cell, taking care of initialisation and destruction.@@ -73,12 +76,12 @@ Migrations will by default not inspect the interior of a 'handling' cell. This means that handles are only migrated if they have exactly the same type. -}-handling-  :: ( Typeable h-     , Monad m-     )-  => Handle m h-  -> Cell (HandlingStateT m) arbitrary h+handling ::+  ( Typeable h+  , Monad m+  ) =>+  Handle m h ->+  Cell (HandlingStateT m) arbitrary h handling handle = arr (const ()) >>> handlingParametrised (toParametrised handle)  {- | Generalisation of 'Handle' carrying an additional parameter which may change at runtime.@@ -94,32 +97,35 @@   }  instance MFunctor (ParametrisedHandle p) where-  hoist morphism ParametrisedHandle { .. } = ParametrisedHandle-    { createParametrised = morphism . createParametrised-    , changeParametrised = ((morphism .) .) . changeParametrised-    , destroyParametrised = (morphism .) . destroyParametrised-    }+  hoist morphism ParametrisedHandle {..} =+    ParametrisedHandle+      { createParametrised = morphism . createParametrised+      , changeParametrised = ((morphism .) .) . changeParametrised+      , destroyParametrised = (morphism .) . destroyParametrised+      } --- | Given the methods 'createParametrised' and 'destroyParametrised',---   build a fitting method for 'changeParametrised' which+{- | Given the methods 'createParametrised' and 'destroyParametrised',+   build a fitting method for 'changeParametrised' which+-} defaultChange :: (Eq p, Monad m) => (p -> m h) -> (p -> h -> m ()) -> p -> p -> h -> m h defaultChange creator destructor pOld pNew h   | pOld == pNew = return h-  | otherwise    = do+  | otherwise = do       destructor pOld h       creator pNew  -- | Like 'combineHandles', but for 'ParametrisedHandle's.-combineParametrisedHandles-  :: Applicative m-  => ParametrisedHandle  p1      m  h1-  -> ParametrisedHandle      p2  m      h2-  -> ParametrisedHandle (p1, p2) m (h1, h2)-combineParametrisedHandles handle1 handle2 = ParametrisedHandle-  { createParametrised = \(p1, p2) -> ( , ) <$> createParametrised handle1 p1 <*> createParametrised handle2 p2-  , changeParametrised = \(pOld1, pOld2) (pNew1, pNew2) (h1, h2) -> ( , ) <$> changeParametrised handle1 pOld1 pNew1 h1 <*> changeParametrised handle2 pOld2 pNew2 h2-  , destroyParametrised = \(p1, p2) (h1, h2) -> destroyParametrised handle1 p1 h1 *> destroyParametrised handle2 p2 h2-  }+combineParametrisedHandles ::+  Applicative m =>+  ParametrisedHandle p1 m h1 ->+  ParametrisedHandle p2 m h2 ->+  ParametrisedHandle (p1, p2) m (h1, h2)+combineParametrisedHandles handle1 handle2 =+  ParametrisedHandle+    { createParametrised = \(p1, p2) -> (,) <$> createParametrised handle1 p1 <*> createParametrised handle2 p2+    , changeParametrised = \(pOld1, pOld2) (pNew1, pNew2) (h1, h2) -> (,) <$> changeParametrised handle1 pOld1 pNew1 h1 <*> changeParametrised handle2 pOld2 pNew2 h2+    , destroyParametrised = \(p1, p2) (h1, h2) -> destroyParametrised handle1 p1 h1 *> destroyParametrised handle2 p2 h2+    }  {- | Hide a 'ParametrisedHandle' in a cell, taking care of initialisation and destruction.@@ -134,35 +140,38 @@ Migrations will by default not inspect the interior of a 'handling' cell. This means that parametrised handles are only migrated if they have exactly the same type. -}-handlingParametrised-  :: ( Typeable h, Typeable p-     , Monad m-     , Eq p-     )-  => ParametrisedHandle p m h-  -> Cell (HandlingStateT m) p h-handlingParametrised handleImpl@ParametrisedHandle { .. } = Cell { .. }+handlingParametrised ::+  ( Typeable h+  , Typeable p+  , Monad m+  , Eq p+  ) =>+  ParametrisedHandle p m h ->+  Cell (HandlingStateT m) p h+handlingParametrised handleImpl@ParametrisedHandle {..} = Cell {..}   where     cellState = Uninitialized     cellStep Uninitialized parameter = do       mereHandle <- lift $ createParametrised parameter       let handle = (mereHandle, parameter)       key <- register $ destroyParametrised parameter mereHandle-      return (mereHandle, Handling { handle = handle, .. })-    cellStep handling@Handling { handle = (mereHandle, lastParameter), .. } parameter+      return (mereHandle, Initialized Handling {handle = handle, ..})+    cellStep handling@(Initialized Handling {handle = (mereHandle, lastParameter), ..}) parameter       | parameter == lastParameter = do           reregister (destroyParametrised parameter mereHandle) key           return (mereHandle, handling)       | otherwise = do           mereHandle <- lift $ changeParametrised lastParameter parameter mereHandle           reregister (destroyParametrised parameter mereHandle) key-          return (mereHandle, Handling { handle = (mereHandle, parameter), .. })+          return (mereHandle, Initialized Handling {handle = (mereHandle, parameter), ..}) --- | Every 'Handle' is trivially a 'ParametrisedHandle'---   when the parameter is the trivial type.+{- | Every 'Handle' is trivially a 'ParametrisedHandle'+   when the parameter is the trivial type.+-} toParametrised :: Monad m => Handle m h -> ParametrisedHandle () m h-toParametrised Handle { .. } = ParametrisedHandle-  { createParametrised = const create-  , changeParametrised = const $ const return-  , destroyParametrised = const destroy-  }+toParametrised Handle {..} =+  ParametrisedHandle+    { createParametrised = const create+    , changeParametrised = const $ const return+    , destroyParametrised = const destroy+    }
src/LiveCoding/Handle/Examples.hs view
@@ -10,30 +10,36 @@  -- | Create an 'IORef', with no special cleanup action. ioRefHandle :: a -> Handle IO (IORef a)-ioRefHandle a = Handle-  { create = newIORef a-  , destroy = const $ return () -- IORefs are garbage collected-  }+ioRefHandle a =+  Handle+    { create = newIORef a+    , destroy = const $ return () -- IORefs are garbage collected+    }  -- | Create an uninitialised 'MVar', with no special cleanup action. emptyMVarHandle :: Handle IO (MVar a)-emptyMVarHandle = Handle-  { create = newEmptyMVar-  , destroy = const $ return () -- MVars are garbage collected-  }+emptyMVarHandle =+  Handle+    { create = newEmptyMVar+    , destroy = const $ return () -- MVars are garbage collected+    } --- | Create an 'MVar' initialised to some value @a@,---   with no special cleanup action.+{- | Create an 'MVar' initialised to some value @a@,+   with no special cleanup action.+-} newMVarHandle :: a -> Handle IO (MVar a)-newMVarHandle a = Handle-  { create = newMVar a-  , destroy = const $ return () -- MVars are garbage collected-  }+newMVarHandle a =+  Handle+    { create = newMVar a+    , destroy = const $ return () -- MVars are garbage collected+    } --- | Launch a thread executing the given action---   and kill it when the handle is removed.+{- | Launch a thread executing the given action+   and kill it when the handle is removed.+-} threadHandle :: IO () -> Handle IO ThreadId-threadHandle action = Handle-  { create  = forkIO action-  , destroy = killThread-  }+threadHandle action =+  Handle+    { create = forkIO action+    , destroy = killThread+    }
src/LiveCoding/HandlingState.hs view
@@ -6,11 +6,11 @@ module LiveCoding.HandlingState where  -- base-import Control.Arrow (returnA, arr, (>>>))+import Control.Arrow (arr, returnA, (>>>)) import Data.Data  -- transformers-import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.Monad.Trans.Class (MonadTrans (lift)) import Control.Monad.Trans.State.Strict import Data.Foldable (traverse_) @@ -25,41 +25,41 @@ import LiveCoding.LiveProgram import LiveCoding.LiveProgram.Monad.Trans -data Handling h where-  Handling-    :: { key    :: Key-       , handle :: h-       }-    -> Handling h-  Uninitialized :: Handling h+data Handling h = Handling+  { key :: Key+  , handle :: h+  }  type Destructors m = IntMap (Destructor m)  -- | Hold a map of registered handle keys and destructors data HandlingState m = HandlingState-  { nHandles    :: Key+  { nHandles :: Key   , destructors :: Destructors m   }-  deriving Data+  deriving (Data) --- | In this monad, handles can be registered,---   and their destructors automatically executed.---   It is basically a monad in which handles are automatically garbage collected.+{- | In this monad, handles can be registered,+   and their destructors automatically executed.+   It is basically a monad in which handles are automatically garbage collected.+-} type HandlingStateT m = StateT (HandlingState m) m  initHandlingState :: HandlingState m-initHandlingState = HandlingState-  { nHandles = 0-  , destructors = IntMap.empty-  }+initHandlingState =+  HandlingState+    { nHandles = 0+    , destructors = IntMap.empty+    } --- | Handle the 'HandlingStateT' effect _without_ garbage collection.---   Apply this to your main loop after calling 'foreground'.---   Since there is no garbage collection, don't use this function for live coding.-runHandlingStateT-  :: Monad m-  => HandlingStateT m a-  -> m a+{- | Handle the 'HandlingStateT' effect _without_ garbage collection.+   Apply this to your main loop after calling 'foreground'.+   Since there is no garbage collection, don't use this function for live coding.+-}+runHandlingStateT ::+  Monad m =>+  HandlingStateT m a ->+  m a runHandlingStateT = flip evalStateT initHandlingState  {- | Apply this to your main live cell before passing it to the runtime.@@ -73,109 +73,99 @@ 3. Destroy all still unregistered handles    (i.e. those that were removed in the last tick) -}-runHandlingStateC-  :: forall m a b .-     (Monad m, Typeable m)-  => Cell (HandlingStateT m) a b-  -> Cell                 m  a b-runHandlingStateC cell = flip runStateC_ initHandlingState-  $ hoistCellOutput garbageCollected cell+runHandlingStateC ::+  forall m a b.+  (Monad m, Typeable m) =>+  Cell (HandlingStateT m) a b ->+  Cell m a b+runHandlingStateC cell =+  flip runStateC_ initHandlingState $+    hoistCellOutput garbageCollected cell  -- | Like 'runHandlingStateC', but for whole live programs.-runHandlingState-  :: (Monad m, Typeable m)-  => LiveProgram (HandlingStateT m)-  -> LiveProgram                 m-runHandlingState LiveProgram { .. } = flip runStateL initHandlingState LiveProgram-  { liveStep = garbageCollected . liveStep-  , ..-  }+runHandlingState ::+  (Monad m, Typeable m) =>+  LiveProgram (HandlingStateT m) ->+  LiveProgram m+runHandlingState LiveProgram {..} =+  flip+    runStateL+    initHandlingState+    LiveProgram+      { liveStep = garbageCollected . liveStep+      , ..+      } -garbageCollected-  :: Monad m-  => HandlingStateT m a-  -> HandlingStateT m a+garbageCollected ::+  Monad m =>+  HandlingStateT m a ->+  HandlingStateT m a garbageCollected action = unregisterAll >> action <* destroyUnregistered  data Destructor m = Destructor   { isRegistered :: Bool-  , action       :: m ()+  , action :: m ()   } --register-  :: Monad m-  => m () -- ^ Destructor-  -> HandlingStateT m Key+register ::+  Monad m =>+  -- | Destructor+  m () ->+  HandlingStateT m Key register destructor = do-  HandlingState { .. } <- get+  HandlingState {..} <- get   let key = nHandles + 1-  put HandlingState-    { nHandles = key-    , destructors = insertDestructor destructor key destructors-    }+  put+    HandlingState+      { nHandles = key+      , destructors = insertDestructor destructor key destructors+      }   return key -reregister-  :: Monad m-  => m ()-  -> Key-  -> HandlingStateT m ()+reregister ::+  Monad m =>+  m () ->+  Key ->+  HandlingStateT m () reregister action key = do-  HandlingState { .. } <- get-  put HandlingState { destructors = insertDestructor action key destructors, .. }+  HandlingState {..} <- get+  put HandlingState {destructors = insertDestructor action key destructors, ..} -insertDestructor-  :: m ()-  -> Key-  -> Destructors m-  -> Destructors m+insertDestructor ::+  m () ->+  Key ->+  Destructors m ->+  Destructors m insertDestructor action key destructors =-  let destructor = Destructor { isRegistered = True, .. }-  in  insert key destructor destructors+  let destructor = Destructor {isRegistered = True, ..}+   in insert key destructor destructors -unregisterAll-  :: Monad m-  => HandlingStateT m ()+unregisterAll ::+  Monad m =>+  HandlingStateT m () unregisterAll = do-  HandlingState { .. } <- get-  let newDestructors = IntMap.map (\destructor -> destructor { isRegistered = False }) destructors-  put HandlingState { destructors = newDestructors, .. }+  HandlingState {..} <- get+  let newDestructors = IntMap.map (\destructor -> destructor {isRegistered = False}) destructors+  put HandlingState {destructors = newDestructors, ..} -destroyUnregistered-  :: Monad m-  => HandlingStateT m ()+destroyUnregistered ::+  Monad m =>+  HandlingStateT m () destroyUnregistered = do-  HandlingState { .. } <- get+  HandlingState {..} <- get   let-      (registered, unregistered) = partition isRegistered destructors+    (registered, unregistered) = partition isRegistered destructors   traverse_ (lift . action) unregistered-  put HandlingState { destructors = registered, .. }+  put HandlingState {destructors = registered, ..}  -- * 'Data' instances--dataTypeHandling :: DataType-dataTypeHandling = mkDataType "Handling" [handlingConstr, uninitializedConstr]--handlingConstr :: Constr-handlingConstr = mkConstr dataTypeHandling "Handling" [] Prefix--uninitializedConstr :: Constr-uninitializedConstr = mkConstr dataTypeHandling "Uninitialized" [] Prefix--instance (Typeable h) => Data (Handling h) where-  dataTypeOf _ = dataTypeHandling-  toConstr Handling { .. } = handlingConstr-  toConstr Uninitialized = uninitializedConstr-  gunfold _cons nil constructor = nil Uninitialized- dataTypeDestructor :: DataType-dataTypeDestructor = mkDataType "Destructor" [ destructorConstr ]+dataTypeDestructor = mkDataType "Destructor" [destructorConstr]  destructorConstr :: Constr destructorConstr = mkConstr dataTypeDestructor "Destructor" [] Prefix  instance Typeable m => Data (Destructor m) where   dataTypeOf _ = dataTypeDestructor-  toConstr Destructor { .. } = destructorConstr+  toConstr Destructor {..} = destructorConstr   gunfold _ _ = error "Destructor.gunfold"
src/LiveCoding/LiveProgram/Except.hs view
@@ -1,5 +1,6 @@-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RecordWildCards #-}+ {- | Live programs in the @'ExceptT' e m@ monad can stop execution by throwing an exception @e@.  Handling these exceptions is done by realising that live programs in fact form a monad in the exception type.@@ -8,7 +9,7 @@ module LiveCoding.LiveProgram.Except where  -- base-import Control.Monad (liftM, ap)+import Control.Monad (ap, liftM) import Data.Data import Data.Void (Void) @@ -17,12 +18,12 @@ import Control.Monad.Trans.Reader  -- essence-of-live-coding-import LiveCoding.Cell (hoistCell, toLiveCell, liveCell, constM)-import LiveCoding.CellExcept (CellExcept, runCellExcept, once_)+import LiveCoding.Cell (constM, hoistCell, liveCell, toLiveCell)+import LiveCoding.CellExcept (CellExcept, once_, runCellExcept)+import qualified LiveCoding.CellExcept as CellExcept import LiveCoding.Exceptions.Finite (Finite) import LiveCoding.Forever import LiveCoding.LiveProgram-import qualified LiveCoding.CellExcept as CellExcept  {- | A live program that can throw an exception. @@ -39,25 +40,25 @@ and it is in fact a newtype around it. -} newtype LiveProgramExcept m e = LiveProgramExcept-  { unLiveProgramExcept :: CellExcept () () m e }+  {unLiveProgramExcept :: CellExcept () () m e}   deriving (Functor, Applicative, Monad)  -- | Execute a 'LiveProgramExcept', throwing its exceptions in the 'ExceptT' monad.-runLiveProgramExcept-  :: Monad m-  => LiveProgramExcept m e-  -> LiveProgram (ExceptT e m)-runLiveProgramExcept LiveProgramExcept { .. } = liveCell $ runCellExcept unLiveProgramExcept+runLiveProgramExcept ::+  Monad m =>+  LiveProgramExcept m e ->+  LiveProgram (ExceptT e m)+runLiveProgramExcept LiveProgramExcept {..} = liveCell $ runCellExcept unLiveProgramExcept  {- | Lift a 'LiveProgram' into the 'LiveProgramExcept' monad.  Similar to 'LiveProgram.CellExcept.try'. This will execute the live program until it throws an exception. -}-try-  :: (Data e, Finite e, Functor m)-  => LiveProgram (ExceptT e m)-  -> LiveProgramExcept m e+try ::+  (Data e, Finite e, Functor m) =>+  LiveProgram (ExceptT e m) ->+  LiveProgramExcept m e try = LiveProgramExcept . CellExcept.try . toLiveCell  {- | Safely convert to 'LiveProgram's.@@ -66,20 +67,20 @@ no exceptions can be thrown, and thus we can safely assume that it is a 'LiveProgram' in @m@. -}-safely-  :: Monad m-  => LiveProgramExcept m Void-  -> LiveProgram m+safely ::+  Monad m =>+  LiveProgramExcept m Void ->+  LiveProgram m safely = liveCell . CellExcept.safely . unLiveProgramExcept  {- | Run a 'LiveProgram' as a 'LiveProgramExcept'.  This is always safe in the sense that it has no exceptions. -}-safe-  :: Monad m-  => LiveProgram m-  -> LiveProgramExcept m Void+safe ::+  Monad m =>+  LiveProgram m ->+  LiveProgramExcept m Void safe = LiveProgramExcept . CellExcept.safe . toLiveCell  -- | Run a monadic action and immediately raise its result as an exception.@@ -95,19 +96,21 @@ This way, you can create an infinite loop, with the exception as the loop variable. -}-foreverELiveProgram-  :: (Data e, Monad m)-  => e -- ^ The loop initialisation-  -> LiveProgramExcept (ReaderT e m) e -- ^ The live program to execute indefinitely-  -> LiveProgram                  m-foreverELiveProgram e LiveProgramExcept { .. } = liveCell $ foreverE e $ hoistCell commute $ runCellExcept unLiveProgramExcept+foreverELiveProgram ::+  (Data e, Monad m) =>+  -- | The loop initialisation+  e ->+  -- | The live program to execute indefinitely+  LiveProgramExcept (ReaderT e m) e ->+  LiveProgram m+foreverELiveProgram e LiveProgramExcept {..} = liveCell $ foreverE e $ hoistCell commute $ runCellExcept unLiveProgramExcept   where     commute :: ExceptT e (ReaderT r m) a -> ReaderT r (ExceptT e m) a     commute action = ReaderT $ ExceptT . runReaderT (runExceptT action)  -- | Run a 'LiveProgramExcept' in a loop, discarding the exception.-foreverCLiveProgram-  :: (Data e, Monad m)-  => LiveProgramExcept m e-  -> LiveProgram       m-foreverCLiveProgram LiveProgramExcept { .. } = liveCell $ foreverC $ runCellExcept unLiveProgramExcept+foreverCLiveProgram ::+  (Data e, Monad m) =>+  LiveProgramExcept m e ->+  LiveProgram m+foreverCLiveProgram LiveProgramExcept {..} = liveCell $ foreverC $ runCellExcept unLiveProgramExcept
src/LiveCoding/LiveProgram/Monad/Trans.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE RecordWildCards #-}+ module LiveCoding.LiveProgram.Monad.Trans where  -- base@@ -9,21 +10,24 @@ import Control.Monad.Trans.State.Strict  -- essence-of-live-coding-import LiveCoding.LiveProgram+ import LiveCoding.Cell.Monad.Trans+import LiveCoding.LiveProgram --- | Remove a stateful effect from the monad stack by supplying the initial state.---   This state then becomes part of the internal live program state,---   and is subject to migration as any other state.---   Live programs are automatically migrated to and from applications of 'runStateL'.-runStateL-  :: (Data stateT, Monad m)-  => LiveProgram (StateT stateT m)-  ->                     stateT-  -> LiveProgram                m-runStateL LiveProgram { .. } stateT = LiveProgram-  { liveState = State { stateInternal = liveState, .. }-  , liveStep = \State { .. } -> do-      (stateInternal, stateT) <- runStateT (liveStep stateInternal) stateT-      return State { .. }-  }+{- | Remove a stateful effect from the monad stack by supplying the initial state.+   This state then becomes part of the internal live program state,+   and is subject to migration as any other state.+   Live programs are automatically migrated to and from applications of 'runStateL'.+-}+runStateL ::+  (Data stateT, Monad m) =>+  LiveProgram (StateT stateT m) ->+  stateT ->+  LiveProgram m+runStateL LiveProgram {..} stateT =+  LiveProgram+    { liveState = State {stateInternal = liveState, ..}+    , liveStep = \State {..} -> do+        (stateInternal, stateT) <- runStateT (liveStep stateInternal) stateT+        return State {..}+    }
src/LiveCoding/Migrate/Cell.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-}+ module LiveCoding.Migrate.Cell where  -- base@@ -9,46 +10,49 @@ import Data.Generics.Aliases  -- essence-of-live-coding++import Control.Applicative (Alternative ((<|>))) import LiveCoding.Cell import LiveCoding.Cell.Feedback import LiveCoding.Exceptions import LiveCoding.Migrate.Migration-import Control.Applicative (Alternative((<|>)))  -- * Migrations to and from pairs  -- ** Generic migration functions --- | Builds the migration function for a pair, or product type,---   such as tuples, but customisable to your own products.---   You need to pass it the equivalents of 'fst', 'snd', and '(,)'.---   Tries to migrate the value into the first element, then into the second.-maybeMigrateToPair-  :: (Typeable a, Typeable b, Typeable c)-  => (t a b -> a)-  -- ^ The accessor of the first element-  -> (t a b -> b)-  -- ^ The accessor of the second element-  -> (a -> b -> t a b)-  -- ^ The constructor-  -> t a b-  -- ^ The pair-  -> c-  -- ^ The new value for the first or second element-  -> Maybe (t a b)+{- | Builds the migration function for a pair, or product type,+   such as tuples, but customisable to your own products.+   You need to pass it the equivalents of 'fst', 'snd', and '(,)'.+   Tries to migrate the value into the first element, then into the second.+-}+maybeMigrateToPair ::+  (Typeable a, Typeable b, Typeable c) =>+  -- | The accessor of the first element+  (t a b -> a) ->+  -- | The accessor of the second element+  (t a b -> b) ->+  -- | The constructor+  (a -> b -> t a b) ->+  -- | The pair+  t a b ->+  -- | The new value for the first or second element+  c ->+  Maybe (t a b) maybeMigrateToPair fst snd cons pair c = do   flip cons (snd pair) <$> cast c <|> cons (fst pair) <$> cast c --- | Like 'maybeMigrateToPair', but in the other direction.---   Again, it is biased with respect to the first element of the pair.-maybeMigrateFromPair-  :: (Typeable a, Typeable b, Typeable c)-  => (t a b -> a)-  -- ^ The accessor of the first element-  -> (t a b -> b)-  -- ^ The accessor of the second element-  -> t a b-  -> Maybe c+{- | Like 'maybeMigrateToPair', but in the other direction.+   Again, it is biased with respect to the first element of the pair.+-}+maybeMigrateFromPair ::+  (Typeable a, Typeable b, Typeable c) =>+  -- | The accessor of the first element+  (t a b -> a) ->+  -- | The accessor of the second element+  (t a b -> b) ->+  t a b ->+  Maybe c maybeMigrateFromPair fst snd pair = cast (fst pair) <|> cast (snd pair)  -- ** Migrations involving sequential compositions of cells@@ -57,16 +61,15 @@ migrationToComposition :: Migration migrationToComposition = migrationTo2 $ maybeMigrateToPair state1 state2 Composition - -- | Migrate @cell1 >>> cell2@ to @cell1@, and if this fails, to @cell2@. migrationFromComposition :: Migration migrationFromComposition = constMigrationFrom2 $ maybeMigrateFromPair state1 state2  -- | Combines all migrations related to composition, favouring migration to compositions. migrationComposition :: Migration-migrationComposition-  =  migrationToComposition-  <> migrationFromComposition+migrationComposition =+  migrationToComposition+    <> migrationFromComposition  -- ** Migrations involving parallel compositions of cells @@ -80,9 +83,9 @@  -- | Combines all migrations related to parallel composition, favouring migration to parallel composition. migrationParallel :: Migration-migrationParallel-  =  migrationToParallel-  <> migrationFromParallel+migrationParallel =+  migrationToParallel+    <> migrationFromParallel  -- ** Migration involving 'ArrowChoice' @@ -96,9 +99,9 @@  -- | Combines all migrations related to choice, favouring migration to choice. migrationChoice :: Migration-migrationChoice-  =  migrationToChoice-  <> migrationFromChoice+migrationChoice =+  migrationToChoice+    <> migrationFromChoice  -- ** Feedback @@ -116,11 +119,11 @@  -- * Control flow -maybeMigrateToExceptState-  :: (Typeable state, Typeable state')-  => ExceptState state e-  ->             state'-  -> Maybe (ExceptState state e)+maybeMigrateToExceptState ::+  (Typeable state, Typeable state') =>+  ExceptState state e ->+  state' ->+  Maybe (ExceptState state e) maybeMigrateToExceptState (NotThrown _) state = NotThrown <$> cast state maybeMigrateToExceptState (Exception e) _ = Just $ Exception e @@ -128,10 +131,10 @@ migrationToExceptState :: Migration migrationToExceptState = migrationTo2 maybeMigrateToExceptState -maybeMigrateFromExceptState-  :: (Typeable state, Typeable state')-  => ExceptState state e-  -> Maybe       state'+maybeMigrateFromExceptState ::+  (Typeable state, Typeable state') =>+  ExceptState state e ->+  Maybe state' maybeMigrateFromExceptState (NotThrown state) = cast state maybeMigrateFromExceptState (Exception e) = Nothing @@ -147,9 +150,9 @@  -- | Combines all 'Cell'-related migrations. migrationCell :: Migration-migrationCell-  =  migrationComposition-  <> migrationParallel-  <> migrationChoice-  <> migrationExceptState-  <> migrationFeedback+migrationCell =+  migrationComposition+    <> migrationParallel+    <> migrationChoice+    <> migrationExceptState+    <> migrationFeedback
src/LiveCoding/Migrate/Debugger.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-}+ module LiveCoding.Migrate.Debugger where  -- base@@ -9,24 +10,24 @@ import LiveCoding.Debugger import LiveCoding.Migrate.Migration -maybeMigrateToDebugging-  :: (Typeable state', Typeable state)-  => Debugging dbgState state-  -> state'-  -> Maybe (Debugging dbgState state)-maybeMigrateToDebugging Debugging { dbgState } state' = do+maybeMigrateToDebugging ::+  (Typeable state', Typeable state) =>+  Debugging dbgState state ->+  state' ->+  Maybe (Debugging dbgState state)+maybeMigrateToDebugging Debugging {dbgState} state' = do   state <- cast state'-  return Debugging { .. }+  return Debugging {..}  -- | Tries to cast the current state into the joint state of debugger and program. migrationToDebugging :: Migration migrationToDebugging = migrationTo2 maybeMigrateToDebugging -maybeMigrateFromDebugging-  :: (Typeable state', Typeable state)-  => Debugging dbgState state-  -> Maybe              state'-maybeMigrateFromDebugging Debugging { state } = cast state+maybeMigrateFromDebugging ::+  (Typeable state', Typeable state) =>+  Debugging dbgState state ->+  Maybe state'+maybeMigrateFromDebugging Debugging {state} = cast state  -- | Try to extract a state from the current joint state of debugger and program. migrationFromDebugging :: Migration
src/LiveCoding/Migrate/Migration.hs view
@@ -13,20 +13,23 @@ import Data.Generics.Schemes (glength)  data Migration = Migration-  { runMigration :: forall a b . (Data a, Data b) => a -> b -> Maybe a }+  {runMigration :: forall a b. (Data a, Data b) => a -> b -> Maybe a}  -- | Run a migration and insert the new initial state in case of failure.-runSafeMigration-  :: (Data a, Data b)-  => Migration-  -> a -> b -> a+runSafeMigration ::+  (Data a, Data b) =>+  Migration ->+  a ->+  b ->+  a runSafeMigration migration a b = fromMaybe a $ runMigration migration a b  -- | If both migrations would succeed, the result from the first is used. instance Semigroup Migration where-  migration1 <> migration2 = Migration $ \a b -> getFirst-    $  (First $ runMigration migration1 a b)-    <> (First $ runMigration migration2 a b)+  migration1 <> migration2 = Migration $ \a b ->+    getFirst $+      (First $ runMigration migration1 a b)+        <> (First $ runMigration migration2 a b)  instance Monoid Migration where   mempty = Migration $ const $ const Nothing@@ -45,29 +48,30 @@   -- Try to cast the single child to b   gmapM (const $ cast b) a --- | If you have a specific type that you would like to be migrated to a specific other type,---   you can create a migration for this.---   For example: @userMigration (toInteger :: Int -> Integer)@-userMigration-  :: (Typeable c, Typeable d)-  => (c -> d)-  -> Migration+{- | If you have a specific type that you would like to be migrated to a specific other type,+   you can create a migration for this.+   For example: @userMigration (toInteger :: Int -> Integer)@+-}+userMigration ::+  (Typeable c, Typeable d) =>+  (c -> d) ->+  Migration userMigration specific = Migration $ \_a b -> cast =<< specific <$> cast b -migrationTo2-  :: Typeable t-  => (forall a b c . (Typeable a, Typeable b, Typeable c) => t b c -> a -> Maybe (t b c))-  -> Migration+migrationTo2 ::+  Typeable t =>+  (forall a b c. (Typeable a, Typeable b, Typeable c) => t b c -> a -> Maybe (t b c)) ->+  Migration migrationTo2 f = Migration $ \t a -> ext2M (const Nothing) (flip f a) t -constMigrationFrom2-  :: Typeable t-  => (forall a b c . (Typeable a, Typeable b, Typeable c) => t b c -> Maybe a)-  -> Migration+constMigrationFrom2 ::+  Typeable t =>+  (forall a b c. (Typeable a, Typeable b, Typeable c) => t b c -> Maybe a) ->+  Migration constMigrationFrom2 f = Migration $ \_ t -> ext2Q (const Nothing) f t -migrationTo1-  :: Typeable t-  => (forall a b . (Typeable a, Typeable b) => t b -> a -> Maybe (t b))-  -> Migration+migrationTo1 ::+  Typeable t =>+  (forall a b. (Typeable a, Typeable b) => t b -> a -> Maybe (t b)) ->+  Migration migrationTo1 f = Migration $ \t a -> ext1M (const Nothing) (flip f a) t
src/LiveCoding/Migrate/Monad/Trans.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-}+ module LiveCoding.Migrate.Monad.Trans where  -- base@@ -9,25 +10,26 @@ import LiveCoding.Cell.Monad.Trans import LiveCoding.Migrate.Migration -maybeMigrateToState-  :: (Typeable stateInternal', Typeable stateInternal)-  => State stateT stateInternal-  -> stateInternal'-  -> Maybe (State stateT stateInternal)-maybeMigrateToState State { stateT } stateInternal' = do+maybeMigrateToState ::+  (Typeable stateInternal', Typeable stateInternal) =>+  State stateT stateInternal ->+  stateInternal' ->+  Maybe (State stateT stateInternal)+maybeMigrateToState State {stateT} stateInternal' = do   stateInternal <- cast stateInternal'-  return State { .. }+  return State {..} --- | Tries to cast the current state into the joint state of a program---   where a state effect has been absorbed into the internal state with 'runStateL' or 'runStateC'.+{- | Tries to cast the current state into the joint state of a program+   where a state effect has been absorbed into the internal state with 'runStateL' or 'runStateC'.+-} migrationToState :: Migration migrationToState = migrationTo2 maybeMigrateToState -maybeMigrateFromState-  :: (Typeable stateInternal', Typeable stateInternal)-  => State stateT stateInternal-  -> Maybe              stateInternal'-maybeMigrateFromState State { stateInternal } = cast stateInternal+maybeMigrateFromState ::+  (Typeable stateInternal', Typeable stateInternal) =>+  State stateT stateInternal ->+  Maybe stateInternal'+maybeMigrateFromState State {stateInternal} = cast stateInternal  -- | Try to extract a state from the current joint state of a program wrapped with 'runStateL' or 'runStateC'. migrationFromState :: Migration
+ src/LiveCoding/Migrate/NoMigration.hs view
@@ -0,0 +1,111 @@+{-# LANGUAGE Arrows #-}+{-# LANGUAGE DeriveTraversable #-}+{-# LANGUAGE RecordWildCards #-}++{- |+Module      : LiveCoding.Migrate.NoMigration+Description : Mechanism to save state in a Cell without requiring a Data instance.++If a data type is wrapped in 'NoMigration' then it can be used as the state of a 'Cell'+without requiring it to have a 'Data' instance. The consequence is that if the type has changed+in between a livereload, then the previous saved value will be discarded, and no migration attempt+will happen.++'LiveCoding' does not export 'delay' and 'changes' from this module. These functions should be+used with a qualified import.+-}+module LiveCoding.Migrate.NoMigration where++-- base++import Control.Arrow (Arrow (arr, second), returnA, (>>>))+import Control.Monad (guard)+import Data.Data (+  Constr,+  Data (dataTypeOf, gunfold, toConstr),+  DataType,+  Fixity (Prefix),+  Typeable,+  mkConstr,+  mkDataType,+ )++-- essence-of-live-coding+import LiveCoding.Cell+import qualified LiveCoding.Cell.Feedback as Feedback+import LiveCoding.Cell.Monad (hoistCellKleisli)++-- * 'NoMigration' data type and 'Data' instance.++{- | Isomorphic to @'Maybe' a@ but has a different 'Data' instance. The 'Data' instance for @'NoMigration' a@ doesn't require a 'Data' instance for @a@.++ If a data type is wrapped in 'NoMigration' then it can be used as the state of a 'Cell'+ without requiring it to have a 'Data' instance. The consequence is that if the type has changed+ in between a livereload, then the previous saved value will be discarded, and no migration attempt+ will happen.+-}+data NoMigration a = Initialized a | Uninitialized+  deriving (Show, Eq, Functor, Foldable, Traversable)++fromNoMigration :: a -> NoMigration a -> a+fromNoMigration _ (Initialized a) = a+fromNoMigration a Uninitialized = a++dataTypeNoMigration :: DataType+dataTypeNoMigration = mkDataType "NoMigration" [initializedConstr, uninitializedConstr]++initializedConstr :: Constr+initializedConstr = mkConstr dataTypeNoMigration "Initialized" [] Prefix++uninitializedConstr :: Constr+uninitializedConstr = mkConstr dataTypeNoMigration "Uninitialized" [] Prefix++-- |The Data instance for @'NoMigration' a@ doesn't require a 'Data' instance for @a@.+instance (Typeable a) => Data (NoMigration a) where+  dataTypeOf _ = dataTypeNoMigration+  toConstr (Initialized _) = initializedConstr+  toConstr Uninitialized = uninitializedConstr+  gunfold _cons nil _ = nil Uninitialized++-- * Utility functions which internally use 'NoMigration'.++{- | Like 'Feedback.delay', but doesn't require 'Data' instance, and only migrates the+ last value if it still has the same type.+-}+delay :: (Monad m, Typeable a) => a -> Cell m a a+delay a = arr Initialized >>> Feedback.delay Uninitialized >>> arr (fromNoMigration a)++{- | Like 'Utils.changes', but doesn't require Data instance, and only migrates the last+ value if it still is of the same type.+-}+changes :: (Typeable a, Eq a, Monad m) => Cell m a (Maybe a)+changes = proc a -> do+  aLast <- delay Nothing -< Just a+  returnA+    -< do+      aLast' <- aLast+      guard $ a /= aLast'+      return a++{- | Caching version of 'arrM'.++   Only runs the computation in @m@ when the input value+   changes. Meanwhile it keeps outputing the last outputted value. Also runs the computation+   on the first tick. Does not require 'Data' instance. On `:livereload` will run action again on+   first tick.+-}+arrChangesM :: (Monad m, Typeable a, Typeable b, Eq a) => (a -> m b) -> Cell m a b+arrChangesM f = Cell {cellState = Uninitialized, ..}+  where+    cellStep Uninitialized a = h a+    cellStep (Initialized (a', b)) a =+      if a == a'+        then return (b, Initialized (a, b))+        else h a+    h a = (\b' -> (b', Initialized (a, b'))) <$> f a++cellNoMigration :: (Typeable s, Functor m) => s -> (s -> a -> m (b, s)) -> Cell m a b+cellNoMigration state step = Cell {cellState = Uninitialized, ..}+  where+    cellStep Uninitialized a = second Initialized <$> step state a+    cellStep (Initialized s) a = second Initialized <$> step s a
src/LiveCoding/Preliminary/CellExcept/Applicative.lhs view
@@ -37,6 +37,10 @@ \end{code}  \begin{comment}+\begin{code}+cell1 `andThen` cell2 = cell1 `andThen` toCell cell2+\end{code}+ \begin{spec}   hoistCell readException cell2   where
src/LiveCoding/RuntimeIO/Launch.hs view
@@ -1,8 +1,9 @@+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE RankNTypes #-}-{-# LANGUAGE TypeApplications #-}-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TypeApplications #-}+ module LiveCoding.RuntimeIO.Launch where  -- base@@ -11,18 +12,20 @@ import Data.Data  -- transformers-import Control.Monad.Trans.State.Strict+ import Control.Monad.Trans.Except+import Control.Monad.Trans.State.Strict  -- essence-of-live-coding++import LiveCoding.Cell.Monad.Trans import LiveCoding.Debugger+import LiveCoding.Exceptions.Finite (Finite) import LiveCoding.Handle+import LiveCoding.HandlingState import LiveCoding.LiveProgram import LiveCoding.LiveProgram.Except import LiveCoding.LiveProgram.HotCodeSwap-import LiveCoding.Cell.Monad.Trans-import LiveCoding.Exceptions.Finite (Finite)-import LiveCoding.HandlingState  {- | Monads in which live programs can be launched in 'IO', for example when you have special effects that have to be handled on every reload.@@ -39,8 +42,9 @@ instance (Typeable m, Launchable m) => Launchable (HandlingStateT m) where   runIO = runIO . runHandlingState --- | Upon an exception, the program is restarted.---   To handle or log the exception, see "LiveCoding.LiveProgram.Except".+{- | Upon an exception, the program is restarted.+   To handle or log the exception, see "LiveCoding.LiveProgram.Except".+-} instance (Data e, Finite e, Launchable m) => Launchable (ExceptT e m) where   runIO liveProgram = runIO $ foreverCLiveProgram $ try liveProgram @@ -54,22 +58,22 @@ main = liveMain liveProgram @ -}-liveMain-  :: Launchable m-  => LiveProgram m-  -> IO ()+liveMain ::+  Launchable m =>+  LiveProgram m ->+  IO () liveMain = foreground . runIO  -- | Launch a 'LiveProgram' in the foreground thread (blocking). foreground :: Monad m => LiveProgram m -> m ()-foreground liveProgram-  =   stepProgram liveProgram-  >>= foreground+foreground liveProgram =+  stepProgram liveProgram+    >>= foreground  -- | A launched 'LiveProgram' and the thread in which it is running. data LaunchedProgram (m :: * -> *) = LaunchedProgram   { programVar :: MVar (LiveProgram IO)-  , threadId   :: ThreadId+  , threadId :: ThreadId   }  {- | Launch a 'LiveProgram' in a separate thread.@@ -78,23 +82,24 @@ The 'ThreadId' represents the thread where the program runs in. You're advised not to kill it directly, but to run 'stop' instead. -}-launch-  :: Launchable m-  => LiveProgram m-  -> IO (LaunchedProgram m)+launch ::+  Launchable m =>+  LiveProgram m ->+  IO (LaunchedProgram m) launch liveProg = do   programVar <- newMVar $ runIO liveProg   threadId <- forkIO $ background programVar-  return LaunchedProgram { .. }+  return LaunchedProgram {..}  -- | Migrate (using 'hotCodeSwap') the 'LiveProgram' to a new version.-update-  :: Launchable m-  => LaunchedProgram m-  -> LiveProgram     m-  -> IO ()-update LaunchedProgram { .. } newProg = modifyMVarMasked_ programVar-  $ return . hotCodeSwap (runIO newProg)+update ::+  Launchable m =>+  LaunchedProgram m ->+  LiveProgram m ->+  IO ()+update LaunchedProgram {..} newProg =+  modifyMVarMasked_ programVar $+    return . hotCodeSwap (runIO newProg)  {- | Stops a thread where a 'LiveProgram' is being executed. @@ -102,27 +107,27 @@ This can be used to call cleanup actions encoded in the monad, such as 'HandlingStateT'. -}-stop-  :: Launchable m-  => LaunchedProgram m-  -> IO ()-stop launchedProgram@LaunchedProgram { .. } = do+stop ::+  Launchable m =>+  LaunchedProgram m ->+  IO ()+stop launchedProgram@LaunchedProgram {..} = do   update launchedProgram mempty   stepLaunchedProgram launchedProgram   killThread threadId  -- | Launch a 'LiveProgram', but first attach a debugger to it.-launchWithDebugger-  :: (Monad m, Launchable m)-  => LiveProgram m-  -> Debugger m-  -> IO (LaunchedProgram m)+launchWithDebugger ::+  (Monad m, Launchable m) =>+  LiveProgram m ->+  Debugger m ->+  IO (LaunchedProgram m) launchWithDebugger liveProg debugger = launch $ liveProg `withDebugger` debugger  -- | This is the background task executed by 'launch'. background :: MVar (LiveProgram IO) -> IO () background var = forever $ do-  liveProg  <- takeMVar var+  liveProg <- takeMVar var   liveProg' <- stepProgram liveProg   putMVar var liveProg' @@ -130,11 +135,11 @@ stepProgram :: Monad m => LiveProgram m -> m (LiveProgram m) stepProgram LiveProgram {..} = do   liveState' <- liveStep liveState-  return LiveProgram { liveState = liveState', .. }+  return LiveProgram {liveState = liveState', ..}  -- | Advance a launched 'LiveProgram' by a single step and store the result.-stepLaunchedProgram-  :: (Monad m, Launchable m)-  => LaunchedProgram m-  -> IO ()-stepLaunchedProgram LaunchedProgram { .. } = modifyMVarMasked_ programVar stepProgram+stepLaunchedProgram ::+  (Monad m, Launchable m) =>+  LaunchedProgram m ->+  IO ()+stepLaunchedProgram LaunchedProgram {..} = modifyMVarMasked_ programVar stepProgram
test/Cell.hs view
@@ -1,10 +1,12 @@ {-# LANGUAGE ScopedTypeVariables #-}+ module Cell where  -- base-import Prelude hiding (id)+ import Control.Category import Data.Functor.Identity+import Prelude hiding (id)  -- transformers import Control.Monad.Trans.Identity@@ -21,12 +23,18 @@ -- essence-of-live-coding import LiveCoding -import qualified Cell.Util import qualified Cell.Monad.Trans+import qualified Cell.Util -test = testGroup "Cell"-  [ testProperty "steps produces outputs"-    $ \(inputs :: [Int]) -> inputs === fst (runIdentity $ steps (id :: Cell Identity Int Int) inputs)-  , Cell.Util.test-  , Cell.Monad.Trans.test-  ]+test =+  testGroup+    "Cell"+    [ testProperty "steps produces outputs" $+        \(inputs :: [Int]) -> inputs === fst (runIdentity $ steps (id :: Cell Identity Int Int) inputs)+    , testProperty "sumC works as expected" $+        forAll (vector 100) $ \(inputs :: [Int]) ->+          sum (init inputs)+            === last (fst (runIdentity $ steps (sumC :: Cell Identity Int Int) inputs))+    , Cell.Util.test+    , Cell.Monad.Trans.test+    ]
test/Cell/Monad/Trans.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE Arrows #-} {-# LANGUAGE ScopedTypeVariables #-}+ module Cell.Monad.Trans where  -- transformers@@ -19,8 +20,10 @@  import Util -test = testGroup "Cell.Monad.Trans"-  [ testProperty "readerC" $ inIdentityT $ proc (n :: Int) -> do-      nReader <- runReaderC' $ constM ask -< (n, ())-      returnA -< n === nReader-  ]+test =+  testGroup+    "Cell.Monad.Trans"+    [ testProperty "readerC" $ inIdentityT $ proc (n :: Int) -> do+        nReader <- runReaderC' $ constM ask -< (n, ())+        returnA -< n === nReader+    ]
test/Cell/Util.hs view
@@ -1,12 +1,15 @@ {-# LANGUAGE Arrows #-}+{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-}+ module Cell.Util where  -- base import qualified Control.Category as C+import Control.Monad import Data.Functor.Identity+import Data.List import Data.Maybe-import Control.Monad  -- transformers import Control.Monad.Trans.Reader@@ -25,88 +28,168 @@  import Util -test = testGroup "Utility unit tests"---   [ testProperty "Buffer works as expected" CellSimulation---       { cell = buffer---       , input = []---     --   , input =---     --       [ []---     --       , [Pop]---     --       , [Push (23 :: Int)]---     --       , []---     --       , [Pop, Pop]---     --       , [Push 42, Pop]---     --       , [Push 1, Push 2]---     --       , []---     --       , []---     --       , [Pop, Push 3]---     --       , []---     --       , [Pop]---     --       , [Pop]---     --       , []---     --       ]---       , output = []---     --   , output =---     --       [ Nothing---     --       , Nothing---     --       , Just 23---     --       , Just 23---     --       , Nothing---     --       , Nothing---     --       , Just 1---     --       , Just 1---     --       , Just 1---     --       , Just 2---     --       , Just 2---     --       , Just 3---     --       , Nothing---     --       , Nothing---     --       ]---       }-  [ testProperty "buffered works as expected" CellSimulation-    { cell = buffered C.id-    , input =-        [ Just (23 :: Int)-        ]-    , output =-        [ Nothing-        ]-    }-  , testProperty "buffered can be used in an asynchronous setting" $ do-      jointInputs <- arbitrary -- Simulates when input arrives and when inner cell is activated-      -- Make sure cell is ticked at the last time, and no new input arrives-      let innerCell = proc (aMaybe :: Maybe Int) -> do-            isScheduled <- constM ask -< ()-            returnA -< guard isScheduled >> aMaybe-          outerCell = buffered innerCell-          (outputs, _) = runIdentity $ steps (runReaderC' outerCell) jointInputs-          labelString = unwords [show jointInputs, show outputs, show $ length jointInputs, show $ length outputs]-          inputs = snd $ unzip jointInputs-          bufferNotEmpty = isJust $ listToMaybe $ reverse outputs-      -- Make sure each message arrived exactly once, in order-      return-        $ counterexample labelString-        $ catMaybes inputs === catMaybes outputs-        .||. bufferNotEmpty-  , testProperty "delay a >>> changes >>> hold a == delay a"-    $ \(inputs :: [Int]) (startValue :: Int) -> fst (runIdentity $ steps (delay startValue) inputs) === -        fst (runIdentity $ steps (delay startValue >>> changes >>> hold startValue) inputs)-  , testProperty "changes applied to a cell that outputs a constant, always outputs Nothing"-    $ \(value :: Int) (inputs :: [Int]) -> [] === -        catMaybes (fst (runIdentity $ steps (arr (const value) >>> changes) inputs))-  , testProperty "changes works as expected" CellSimulation-    { cell = changes-    , input =-        [ 1 :: Int-        , 1 :: Int-        , 2 :: Int-        , 2 :: Int-        ]-    , output =-        [ Nothing-        , Nothing-        , Just (2 :: Int)-        , Nothing-        ]-    }            -  ]+test =+  testGroup+    "Utility unit tests"+    --   [ testProperty "Buffer works as expected" CellSimulation+    --       { cell = buffer+    --       , input = []+    --     --   , input =+    --     --       [ []+    --     --       , [Pop]+    --     --       , [Push (23 :: Int)]+    --     --       , []+    --     --       , [Pop, Pop]+    --     --       , [Push 42, Pop]+    --     --       , [Push 1, Push 2]+    --     --       , []+    --     --       , []+    --     --       , [Pop, Push 3]+    --     --       , []+    --     --       , [Pop]+    --     --       , [Pop]+    --     --       , []+    --     --       ]+    --       , output = []+    --     --   , output =+    --     --       [ Nothing+    --     --       , Nothing+    --     --       , Just 23+    --     --       , Just 23+    --     --       , Nothing+    --     --       , Nothing+    --     --       , Just 1+    --     --       , Just 1+    --     --       , Just 1+    --     --       , Just 2+    --     --       , Just 2+    --     --       , Just 3+    --     --       , Nothing+    --     --       , Nothing+    --     --       ]+    --       }+    [ testProperty+        "buffered works as expected"+        CellSimulation+          { cell = buffered C.id+          , input =+              [ Just (23 :: Int)+              ]+          , output =+              [ Nothing+              ]+          }+    , testProperty "buffered can be used in an asynchronous setting" $ do+        jointInputs <- arbitrary -- Simulates when input arrives and when inner cell is activated+        -- Make sure cell is ticked at the last time, and no new input arrives+        let innerCell = proc (aMaybe :: Maybe Int) -> do+              isScheduled <- constM ask -< ()+              returnA -< guard isScheduled >> aMaybe+            outerCell = buffered innerCell+            (outputs, _) = runIdentity $ steps (runReaderC' outerCell) jointInputs+            labelString = unwords [show jointInputs, show outputs, show $ length jointInputs, show $ length outputs]+            inputs = snd $ unzip jointInputs+            bufferNotEmpty = isJust $ listToMaybe $ reverse outputs+        -- Make sure each message arrived exactly once, in order+        return $+          counterexample labelString $+            catMaybes inputs === catMaybes outputs+              .||. bufferNotEmpty+    , testProperty "delay a >>> changes >>> hold a == delay a" $+        \(inputs :: [Int]) (startValue :: Int) ->+          fst (runIdentity $ steps (delay startValue) inputs)+            === fst (runIdentity $ steps (delay startValue >>> changes >>> hold startValue) inputs)+    , testProperty "changes applied to a cell that outputs a constant, always outputs Nothing" $+        \(value :: Int) (inputs :: [Int]) ->+          []+            === catMaybes (fst (runIdentity $ steps (arr (const value) >>> changes) inputs))+    , testProperty+        "changes works as expected"+        CellSimulation+          { cell = changes+          , input =+              [ 1 :: Int+              , 1 :: Int+              , 2 :: Int+              , 2 :: Int+              ]+          , output =+              [ Nothing+              , Nothing+              , Just (2 :: Int)+              , Nothing+              ]+          }+    , testProperty+        "changes migrates correctly to itself"+        CellMigrationSimulation+          { cell1 = changes+          , cell2 = changes+          , input1 = [1, 2] :: [Int]+          , input2 = [3, 4] :: [Int]+          , output1 = [Nothing, Just 2]+          , output2 = [Just 3, Just 4]+          }+    , testProperty+        "delay migrates correctly to itself"+        CellMigrationSimulation+          { cell1 = LiveCoding.delay 0+          , cell2 = LiveCoding.delay 0+          , input1 = [1 :: Int, 2, 3, 4]+          , input2 = [5 :: Int, 6, 7, 8]+          , output1 = [0, 1, 2, 3]+          , output2 = [4, 5, 6, 7]+          }+    , testProperty+        "delay migrates correctly with original type wrapped in data type with single constructor"+        CellMigrationSimulation+          { cell1 = LiveCoding.delay 0 :: Cell Identity Int Int+          , cell2 = arr Stuff >>> LiveCoding.delay (Stuff 99) >>> arr (\(Stuff a) -> a) :: Cell Identity Int Int+          , input1 = [1, 2, 3, 4] :: [Int]+          , input2 = [10, 10, 10, 10] :: [Int]+          , output1 = [0, 1, 2, 3] :: [Int]+          , output2 = [4, 10, 10, 10] :: [Int]+          }+    , testProperty "resampleListPar works as expected" $+        forAll (vector 100) $ \(inputs :: [(Int, Int)]) ->+          let+            inputs' = fmap pairToList inputs+            pairToList :: (a, a) -> [a]+            pairToList (x, y) = [x, y]+           in+            CellSimulation+              { cell = resampleListPar (sumC :: Cell Identity Int Int)+              , input = inputs'+              , output = fmap sum . transpose <$> [[0 :: Int, 0]] : tail (inits (init inputs'))+              }+    , testProperty+        "resampleListPar grow"+        CellSimulation+          { cell = resampleListPar (sumC :: Cell Identity Int Int)+          , input = [[1, 1, 1], [1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1, 1]]+          , output = [[0, 0, 0], [1, 1, 1], [2, 2, 2, 0], [3, 3, 3, 1], [4, 4, 4, 2, 0]]+          }+    , testProperty+        "resampleListPar shrink"+        CellSimulation+          { cell = resampleListPar (sumC :: Cell Identity Int Int)+          , input = [[1, 1, 1], [1, 1, 1], [1, 1], [1, 1], [1], []]+          , output = [[0, 0, 0], [1, 1, 1], [2, 2], [3, 3], [4], []]+          }+    , testProperty+        "resampleListPar grow then shrink"+        CellSimulation+          { cell = resampleListPar (sumC :: Cell Identity Int Int)+          , input = [[1, 1, 1], [1, 1, 1, 1], [1, 1, 1]]+          , output = [[0, 0, 0], [1, 1, 1, 0], [2, 2, 2]]+          }+    , testProperty+        "resampleListPar shrink then grow"+        CellSimulation+          { cell = resampleListPar (sumC :: Cell Identity Int Int)+          , input = [[1, 1, 1], [1, 1], [1, 1, 1]]+          , output = [[0, 0, 0], [1, 1], [2, 2, 0]]+          }+    ]++data Stuff a = Stuff a deriving (Eq, Data)
test/Feedback.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE Arrows #-} {-# LANGUAGE RecordWildCards #-}+ module Feedback where  -- essence-of-live-coding@@ -18,30 +19,37 @@ import LiveCoding  constCell :: Monad m => Int -> Cell m () Int-constCell cellState = Cell-  { cellStep = \state _ -> return (state, state)-  , ..-  }+constCell cellState =+  Cell+    { cellStep = \state _ -> return (state, state)+    , ..+    } -test = testGroup "Feedback"-  [ testProperty "Migrates into feedback" CellMigrationSimulation-      { cell1 = constCell 23-      , cell2 = feedback [] $ proc ((), ns) -> do-          n <- constCell 42 -< ()-          returnA -< (sum ns, n : ns)-      , input1 = replicate 3 ()-      , input2 = replicate 3 ()-      , output1 = [23, 23, 23]-      , output2 = [0, 23, 46]-      }-  , testProperty "Migrates out of feedback" CellMigrationSimulation-      { cell1 = feedback [] $ proc ((), ns) -> do-          n <- constCell 23 -< ()-          returnA -< (sum ns, n : ns)-      , cell2 = constCell 42-      , input1 = replicate 3 ()-      , input2 = replicate 3 ()-      , output1 = [0, 23, 46]-      , output2 = [23, 23, 23]-      }-  ]+test =+  testGroup+    "Feedback"+    [ testProperty+        "Migrates into feedback"+        CellMigrationSimulation+          { cell1 = constCell 23+          , cell2 = feedback [] $ proc ((), ns) -> do+              n <- constCell 42 -< ()+              returnA -< (sum ns, n : ns)+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = [23, 23, 23]+          , output2 = [0, 23, 46]+          }+    , testProperty+        "Migrates out of feedback"+        CellMigrationSimulation+          { cell1 = feedback [] $ proc ((), ns) -> do+              n <- constCell 23 -< ()+              returnA -< (sum ns, n : ns)+          , cell2 = constCell 42+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = [0, 23, 46]+          , output2 = [23, 23, 23]+          }+    ]
test/Handle.hs view
@@ -5,12 +5,15 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeApplications #-}+ module Handle where  -- base import Control.Arrow import Data.Functor import Data.Functor.Identity+import GHC.Natural (naturalToInteger)+import GHC.TypeNats (KnownNat, Nat, natVal)  -- transformers import Control.Monad.Trans.Class (lift)@@ -26,32 +29,32 @@ import qualified Handle.LiveProgram import LiveCoding import Util-import GHC.Base (Symbol, Nat)-import GHC.TypeNats (natVal, KnownNat)-import GHC.Natural (naturalToInteger)  -- One day replace State Int with Writer [String] testHandle :: Handle (State Int) String-testHandle = Handle-  { create = do-      n <- get-      return $ "Handle #" ++ show n-  , destroy = const $ put 10000-  }+testHandle =+  Handle+    { create = do+        n <- get+        return $ "Handle #" ++ show n+    , destroy = const $ put 10000+    }  testUnitHandle :: Handle (State Int) ()-testUnitHandle = Handle-  { create = return ()-  , destroy = const $ put 20000-  }+testUnitHandle =+  Handle+    { create = return ()+    , destroy = const $ put 20000+    } -cellWithAction-  :: forall a b . State Int b-  -> Cell Identity a (String, Int)+cellWithAction ::+  forall a b.+  State Int b ->+  Cell Identity a (String, Int) cellWithAction action = flip runStateC 0 $ runHandlingStateC $ handling testHandle >>> arrM (<$ lift action)  testParametrisedHandle :: ParametrisedHandle Bool (State Int) String-testParametrisedHandle = ParametrisedHandle { .. }+testParametrisedHandle = ParametrisedHandle {..}   where     createParametrised flag = do       n <- get@@ -60,13 +63,14 @@     destroyParametrised = const $ const $ put 12345     changeParametrised = defaultChange createParametrised destroyParametrised -cellWithActionParametrized-  :: forall a b . State Int b-  -> Cell Identity Bool (String, Int)-cellWithActionParametrized action-  = flip runStateC 0-  $ runHandlingStateC-  $ handlingParametrised testParametrisedHandle >>> arrM (<$ lift action)+cellWithActionParametrized ::+  forall a b.+  State Int b ->+  Cell Identity Bool (String, Int)+cellWithActionParametrized action =+  flip runStateC 0 $+    runHandlingStateC $+      handlingParametrised testParametrisedHandle >>> arrM (<$ lift action)  throwAfter2Steps :: Monad m => Cell (ExceptT () m) a Int throwAfter2Steps = arr (const 1) >>> sumC >>> throwIf_ (> 1)@@ -75,117 +79,149 @@   deriving (Eq, Show)  testTypelevelHandle :: KnownNat tag => Handle (State Int) (Tag tag)-testTypelevelHandle = Handle-  { create = return Tag-  , destroy = put . fromInteger . naturalToInteger . natVal-  }+testTypelevelHandle =+  Handle+    { create = return Tag+    , destroy = put . fromInteger . naturalToInteger . natVal+    } -cellWithActionTypelevel-  :: KnownNat tag-  => State Int b-  -> Cell Identity a (Tag tag, Int)-cellWithActionTypelevel action-  = flip runStateC 0-  $ runHandlingStateC-  $ handling testTypelevelHandle >>> arrM (<$ lift action)+cellWithActionTypelevel ::+  KnownNat tag =>+  State Int b ->+  Cell Identity a (Tag tag, Int)+cellWithActionTypelevel action =+  flip runStateC 0 $+    runHandlingStateC $+      handling testTypelevelHandle >>> arrM (<$ lift action) -test = testGroup "Handle"-  [ testProperty "Preserve Handles" CellMigrationSimulation-    { cell1 = cellWithAction $ modify (+ 1)-    , cell2 = cellWithAction $ return ()-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = ("Handle #0", ) <$> [1, 2, 3]-    , output2 = ("Handle #0", ) <$> [3, 3, 3]-    }-  , testProperty "Initialise Handles upon migration" CellMigrationSimulation-    { cell1 = flip runStateC 0 $ constM $ modify (+ 1) >> return ""-    , cell2 = cellWithAction $ return ()-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = ("", ) <$> [1, 2, 3]-    , output2 = ("Handle #3", ) <$> [3, 3, 3]-    }-  , testProperty "Preserve Handles in more complex migration" CellMigrationSimulation-    { cell1 = flip runStateC 22-        $ constM (modify (+ 1)) >>> runHandlingStateC (handling testHandle)-    , cell2 = cellWithAction $ return ()-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = ("Handle #23", ) <$> [23, 24, 25]-    , output2 = ("Handle #23", ) <$> replicate 3 25-    }-  , testProperty "Reinitialise Handles in too complex migration" CellMigrationSimulation-    { cell1 = flip runStateC 22-        $   constM (modify (+ 1))-        >>> constM get >>> sumC >>> sumC-        >>> runHandlingStateC (handling testHandle)-    , cell2 = cellWithAction $ return ()-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = ("Handle #23", ) <$> [23, 24, 25]-    , output2 = ("Handle #25", ) <$> replicate 3 25-    }-  , testProperty "Doesn't crash when handle is introspected by migration" CellMigrationSimulation-    { cell1 = cellWithAction $ return ()-    , cell2 = flip runStateC 0 $ runHandlingStateC-        $ handling testUnitHandle >>> arr (const "")-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = ("Handle #0", ) <$> replicate 3 0-    , output2 = ("", ) <$> replicate 3 10000-    }-  , testProperty "Trigger destructors" CellMigrationSimulation-    { cell1 = cellWithAction $ return ()-    , cell2 = flip runStateC 23-        $ runHandlingStateC $ arr $ const "Done"-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = ("Handle #0", ) <$> replicate 3 0-    , output2 = ("Done", ) <$> replicate 3 10000-    }-  , testProperty "Changing parameters triggers destructors" CellSimulation-    { cell = cellWithActionParametrized $ modify (+ 1)-    , input = [True, True, False, False]-    , output =-        [ ("Ye Olde Handle No 0", 1)-        , ("Ye Olde Handle No 0", 2)-        , ("Crazy new hdl #12345", 12346)-        , ("Crazy new hdl #12345", 12347)-        ]-    }-  , testProperty "Transient control flow does not trigger destructors or constructors" CellSimulation-    { cell = cellWithAction (modify (+ 1)) ||| arr (const ("Nope", 23))-    , input = [Right (), Left (), Left (), Right (), Left ()]-    , output =-        [ ("Nope", 23)-        , ("Handle #0", 1)-        , ("Handle #0", 2)-        , ("Nope", 23)-        , ("Handle #0", 3)-        ]-    }-  , testProperty "Permanent control flow does not trigger destructors or constructors" CellSimulation-    { cell = safely $ do-        void $ try $ throwAfter2Steps >>> arr (const ("Nope", 23))-        void $ try $ throwAfter2Steps >>> liftCell (cellWithAction (modify (+ 1)))-        safe $ arr $ const ("Nope", 23)-    , input = replicate 5 ()-    , output =-        [ ("Nope", 23)-        , ("Nope", 23)-        , ("Handle #0", 1)-        , ("Handle #0", 2)-        , ("Nope", 23)-        ]-    }-  , testProperty "Change of type level tags trigger destructors" CellMigrationSimulation-    { cell1 = (cellWithActionTypelevel @23000 $ modify (+ 1)) >>> arr snd-    , cell2 = (cellWithActionTypelevel @42000 $ modify (+ 2)) >>> arr snd-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = [1, 2, 3]-    , output2 = [23000, 23002, 23004]-    }-  , Handle.LiveProgram.test-  ]+test =+  testGroup+    "Handle"+    [ testProperty+        "Preserve Handles"+        CellMigrationSimulation+          { cell1 = cellWithAction $ modify (+ 1)+          , cell2 = cellWithAction $ return ()+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = ("Handle #0",) <$> [1, 2, 3]+          , output2 = ("Handle #0",) <$> [3, 3, 3]+          }+    , testProperty+        "Initialise Handles upon migration"+        CellMigrationSimulation+          { cell1 = flip runStateC 0 $ constM $ modify (+ 1) >> return ""+          , cell2 = cellWithAction $ return ()+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = ("",) <$> [1, 2, 3]+          , output2 = ("Handle #3",) <$> [3, 3, 3]+          }+    , testProperty+        "Preserve Handles in more complex migration"+        CellMigrationSimulation+          { cell1 =+              flip runStateC 22 $+                constM (modify (+ 1)) >>> runHandlingStateC (handling testHandle)+          , cell2 = cellWithAction $ return ()+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = ("Handle #23",) <$> [23, 24, 25]+          , output2 = ("Handle #23",) <$> replicate 3 25+          }+    , testProperty+        "Reinitialise Handles in too complex migration"+        CellMigrationSimulation+          { cell1 =+              flip runStateC 22 $+                constM (modify (+ 1))+                  >>> constM get+                  >>> sumC+                  >>> sumC+                  >>> runHandlingStateC (handling testHandle)+          , cell2 = cellWithAction $ return ()+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = ("Handle #23",) <$> [23, 24, 25]+          , output2 = ("Handle #25",) <$> replicate 3 25+          }+    , testProperty+        "Doesn't crash when handle is introspected by migration"+        CellMigrationSimulation+          { cell1 = cellWithAction $ return ()+          , cell2 =+              flip runStateC 0 $+                runHandlingStateC $+                  handling testUnitHandle >>> arr (const "")+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = ("Handle #0",) <$> replicate 3 0+          , output2 = ("",) <$> replicate 3 10000+          }+    , testProperty+        "Trigger destructors"+        CellMigrationSimulation+          { cell1 = cellWithAction $ return ()+          , cell2 =+              flip runStateC 23 $+                runHandlingStateC $+                  arr $+                    const "Done"+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = ("Handle #0",) <$> replicate 3 0+          , output2 = ("Done",) <$> replicate 3 10000+          }+    , testProperty+        "Changing parameters triggers destructors"+        CellSimulation+          { cell = cellWithActionParametrized $ modify (+ 1)+          , input = [True, True, False, False]+          , output =+              [ ("Ye Olde Handle No 0", 1)+              , ("Ye Olde Handle No 0", 2)+              , ("Crazy new hdl #12345", 12346)+              , ("Crazy new hdl #12345", 12347)+              ]+          }+    , testProperty+        "Transient control flow does not trigger destructors or constructors"+        CellSimulation+          { cell = cellWithAction (modify (+ 1)) ||| arr (const ("Nope", 23))+          , input = [Right (), Left (), Left (), Right (), Left ()]+          , output =+              [ ("Nope", 23)+              , ("Handle #0", 1)+              , ("Handle #0", 2)+              , ("Nope", 23)+              , ("Handle #0", 3)+              ]+          }+    , testProperty+        "Permanent control flow does not trigger destructors or constructors"+        CellSimulation+          { cell = safely $ do+              void $ try $ throwAfter2Steps >>> arr (const ("Nope", 23))+              void $ try $ throwAfter2Steps >>> liftCell (cellWithAction (modify (+ 1)))+              safe $ arr $ const ("Nope", 23)+          , input = replicate 5 ()+          , output =+              [ ("Nope", 23)+              , ("Nope", 23)+              , ("Handle #0", 1)+              , ("Handle #0", 2)+              , ("Nope", 23)+              ]+          }+    , testProperty+        "Change of type level tags trigger destructors"+        CellMigrationSimulation+          { cell1 = (cellWithActionTypelevel @23000 $ modify (+ 1)) >>> arr snd+          , cell2 = (cellWithActionTypelevel @42000 $ modify (+ 2)) >>> arr snd+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 = [1, 2, 3]+          , output2 = [23000, 23002, 23004]+          }+    , Handle.LiveProgram.test+    ]
test/Handle/LiveProgram.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE RecordWildCards #-}+ module Handle.LiveProgram where  -- base@@ -8,7 +9,7 @@ import qualified Data.IntMap as IntMap  -- transformers-import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.Monad.Trans.Class (MonadTrans (lift)) import Control.Monad.Trans.RWS.Strict (RWS, tell) import qualified Control.Monad.Trans.RWS.Strict as RWS import Control.Monad.Trans.State.Strict@@ -25,32 +26,41 @@ import Util.LiveProgramMigration  testHandle :: Handle (RWS () [String] Int) String-testHandle = Handle-  { create = do-      n <- RWS.get-      let msg = "Handle #" ++ show n-      tell ["Creating " ++ msg]-      return msg-  , destroy = const $ tell ["Destroyed handle"]-  }--test = testGroup "Handle.LiveProgram"-  [ testProperty "Trigger destructors in live program" LiveProgramMigration-    { liveProgram1 = runHandlingState $ liveCell-        $ handling testHandle >>> arrM (lift . tell . return) >>> constM inspectHandlingState-    , liveProgram2 = runHandlingState mempty-    , input1 = replicate 3 ()-    , input2 = replicate 3 ()-    , output1 = ["Creating Handle #0", "Handle #0", "Handles: 1", "Destructors: (1,True)"]-        : replicate 2 ["Handle #0", "Handles: 1", "Destructors: (1,True)"]-    , output2 = [["Destroyed handle"], [], []]-    , initialState = 0+testHandle =+  Handle+    { create = do+        n <- RWS.get+        let msg = "Handle #" ++ show n+        tell ["Creating " ++ msg]+        return msg+    , destroy = const $ tell ["Destroyed handle"]     }-  ]-    where-      inspectHandlingState = do-        HandlingState { .. } <- get-        lift $ tell++test =+  testGroup+    "Handle.LiveProgram"+    [ testProperty+        "Trigger destructors in live program"+        LiveProgramMigration+          { liveProgram1 =+              runHandlingState $+                liveCell $+                  handling testHandle >>> arrM (lift . tell . return) >>> constM inspectHandlingState+          , liveProgram2 = runHandlingState mempty+          , input1 = replicate 3 ()+          , input2 = replicate 3 ()+          , output1 =+              ["Creating Handle #0", "Handle #0", "Handles: 1", "Destructors: (1,True)"]+                : replicate 2 ["Handle #0", "Handles: 1", "Destructors: (1,True)"]+          , output2 = [["Destroyed handle"], [], []]+          , initialState = 0+          }+    ]+  where+    inspectHandlingState = do+      HandlingState {..} <- get+      lift $+        tell           [ "Handles: " ++ show nHandles           , "Destructors: " ++ unwords (show . second isRegistered <$> IntMap.toList destructors)           ]
test/Main.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TypeApplications #-} {-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeApplications #-}  -- base import Control.Arrow@@ -20,6 +20,7 @@ import qualified Cell import qualified Feedback import qualified Handle+import qualified Migrate.NoMigration import qualified Monad import qualified Monad.Trans import qualified RuntimeIO.Launch@@ -37,85 +38,105 @@ main = defaultMain tests  tests =-  [ testGroup "Builtin types"-    [ testProperty "Same"-      $ \(x :: Integer) (y :: Integer) -> x === migrate y x-    , testProperty "Different"-      $ \(x :: Integer) (y :: Bool) -> y === migrate y x-    ]-  , testGroup "Product types"-    [ testProperty "Adds default field"-      $ Foo1.foo' === migrate Foo1.foo Foo2.foo-    , testProperty "Keeps only sensible field"-      $ Foo2.foo' === migrate Foo2.foo Foo1.foo-    ]-  , testGroup "Records"-    [ testProperty "Takes record field names into account"-      $ \barA barB barC barC2 barD-      -> Foo2.Bar { barC = barC2, .. } === migrate Foo2.Bar { barC = barC2, .. } Foo1.Bar { .. }-    , testProperty "Migrates nested records"-      $ Foo2.baz' === migrate Foo2.baz Foo1.baz-    ]-  , testGroup "Constructors"-    [ testProperty "Finds correct constructor"-      $ \x y z -> migrate (Foo2.Fooo z) (Foo1.Foo x y) === Foo2.Foo x-    , testProperty "Finds correct constructor with records"-      $ \barA barB barC baarA baarB -> migrate Foo2.Bar { .. } Foo1.Baar { .. } === Foo2.Baar { .. }-    ]-  , testGroup "User migration"-    [ testProperty "Can add migration from Int to Integer"-      $ Foo2.frob' === migrateWith (userMigration intToInteger) Foo2.frob Foo1.frob-    ]-  , testGroup "Newtypes"-    [ testProperty "Wraps into newtype"-      $ \(x :: Integer) -> Foo2.Frob x === migrate Foo2.frob x-    ]-  , testGroup "Debugging"-    [ testProperty "To debugging state"-      $ \(x :: Int) (y :: Int) (z :: Int)-      -> Debugging { dbgState = x, state = y } === migrate Debugging { dbgState = x, state = z } y-    , testProperty "From debugging state"-    $ \(x :: Int) (y :: Int) (z :: Int)-    -> x === migrate y Debugging { dbgState = z, state = x }-    ]-  , testGroup "Cells"-    [ testGroup "Sequential composition"-      [ testProperty "From 1" CellMigrationSimulation-          { cell1 = sumC >>> arr toInteger-          , cell2 = sumC >>> arr toInteger >>> sumC-          , input1 = [1, 1, 1] :: [Int]-          , input2 = [1, 1, 1]-          , output1 = [0, 1, 2]-          , output2 = [0, 3, 7]-          }+  [ testGroup+      "Builtin types"+      [ testProperty "Same" $+          \(x :: Integer) (y :: Integer) -> x === migrate y x+      , testProperty "Different" $+          \(x :: Integer) (y :: Bool) -> y === migrate y x       ]-    , testGroup "Choice"-      [ testProperty "From left" CellMigrationSimulation-        { cell1 = arr fromEither >>> sumC >>> arr toInteger-        , cell2 = (sumC >>> arr toInteger) ||| (arr toInteger >>> sumC)-        , input1 = [Left  1, Right 1, Left  (1 :: Int)]-        , input2 = [Right 1, Left  1, Right 1]-        , output1 = [0, 1, 2]-        , output2 = [0, 3, 1]-        }+  , testGroup+      "Product types"+      [ testProperty "Adds default field" $+          Foo1.foo' === migrate Foo1.foo Foo2.foo+      , testProperty "Keeps only sensible field" $+          Foo2.foo' === migrate Foo2.foo Foo1.foo       ]-    , testGroup "Control flow"-      [ testProperty "Into safe" CellMigrationSimulation-        { cell1 = countFrom 0-        , cell2 = safely $ do-            try $ countFrom 10  >>> throwIf (>  1) ()-            safe $ countFrom 20-        , input1 = replicate 3 ()-        , input2 = replicate 3 ()-        , output1 = [0, 1, 2]-        , output2 = [23, 24, 25]-        }+  , testGroup+      "Records"+      [ testProperty "Takes record field names into account" $+          \barA barB barC barC2 barD ->+            Foo2.Bar {barC = barC2, ..} === migrate Foo2.Bar {barC = barC2, ..} Foo1.Bar {..}+      , testProperty "Migrates nested records" $+          Foo2.baz' === migrate Foo2.baz Foo1.baz       ]-    , Cell.test-    , Handle.test-    , Monad.test-    , Feedback.test-    ]+  , testGroup+      "Constructors"+      [ testProperty "Finds correct constructor" $+          \x y z -> migrate (Foo2.Fooo z) (Foo1.Foo x y) === Foo2.Foo x+      , testProperty "Finds correct constructor with records" $+          \barA barB barC baarA baarB -> migrate Foo2.Bar {..} Foo1.Baar {..} === Foo2.Baar {..}+      , testProperty "Finds correct constructor if type doesn't change" $+          \(x :: Int) -> migrate Nothing (Just x) === Just x+      ]+  , testGroup+      "User migration"+      [ testProperty "Can add migration from Int to Integer" $+          Foo2.frob' === migrateWith (userMigration intToInteger) Foo2.frob Foo1.frob+      ]+  , testGroup+      "Newtypes"+      [ testProperty "Wraps into newtype" $+          \(x :: Integer) -> Foo2.Frob x === migrate Foo2.frob x+      ]+  , testGroup+      "Debugging"+      [ testProperty "To debugging state" $+          \(x :: Int) (y :: Int) (z :: Int) ->+            Debugging {dbgState = x, state = y} === migrate Debugging {dbgState = x, state = z} y+      , testProperty "From debugging state" $+          \(x :: Int) (y :: Int) (z :: Int) ->+            x === migrate y Debugging {dbgState = z, state = x}+      ]+  , testGroup+      "Cells"+      [ testGroup+          "Sequential composition"+          [ testProperty+              "From 1"+              CellMigrationSimulation+                { cell1 = sumC >>> arr toInteger+                , cell2 = sumC >>> arr toInteger >>> sumC+                , input1 = [1, 1, 1] :: [Int]+                , input2 = [1, 1, 1]+                , output1 = [0, 1, 2]+                , output2 = [0, 3, 7]+                }+          ]+      , testGroup+          "Choice"+          [ testProperty+              "From left"+              CellMigrationSimulation+                { cell1 = arr fromEither >>> sumC >>> arr toInteger+                , cell2 = (sumC >>> arr toInteger) ||| (arr toInteger >>> sumC)+                , input1 = [Left 1, Right 1, Left (1 :: Int)]+                , input2 = [Right 1, Left 1, Right 1]+                , output1 = [0, 1, 2]+                , output2 = [0, 3, 1]+                }+          ]+      , testGroup+          "Control flow"+          [ testProperty+              "Into safe"+              CellMigrationSimulation+                { cell1 = countFrom 0+                , cell2 = safely $ do+                    try $ countFrom 10 >>> throwIf (> 1) ()+                    safe $ countFrom 20+                , input1 = replicate 3 ()+                , input2 = replicate 3 ()+                , output1 = [0, 1, 2]+                , output2 = [23, 24, 25]+                }+          ]+      , Cell.test+      , Handle.test+      , Migrate.NoMigration.test+      , Monad.test+      , Feedback.test+      ]   , Monad.Trans.test   , RuntimeIO.Launch.test   ]@@ -123,5 +144,5 @@ countFrom :: Monad m => Int -> Cell m () Int countFrom n = arr (const 1) >>> sumC >>> arr (+ n) -fromEither (Left  a) = a+fromEither (Left a) = a fromEither (Right a) = a
+ test/Migrate/NoMigration.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeApplications #-}++module Migrate.NoMigration where++-- base+import Control.Arrow (Arrow (arr), (>>>))+import Data.Data (Data)+import Data.Maybe (fromJust)++-- test-framework+import Test.Framework (testGroup)++-- test-framework-quickcheck2+import Test.Framework.Providers.QuickCheck2 (testProperty)++-- essence-of-live-coding++import qualified LiveCoding.Migrate.NoMigration as NoMigration+import Util++data Stuff a = Stuff a deriving (Eq, Data)++test =+  testGroup+    "NoMigration unit tests"+    [ testProperty+        "LiveCoding.Migrate.NoMigration.delay migrates correctly to itself"+        CellMigrationSimulation+          { cell1 = NoMigration.delay 0+          , cell2 = NoMigration.delay 0+          , input1 = [1 :: Int, 2, 3, 4]+          , input2 = [5 :: Int, 6, 7, 8]+          , output1 = [0, 1, 2, 3]+          , output2 = [4, 5, 6, 7]+          }+    , testProperty+        "LiveCoding.Migrate.NoMigration.delay different type will not migrate"+        CellMigrationSimulation+          { cell1 = NoMigration.delay 0+          , cell2 = arr Stuff >>> NoMigration.delay (Stuff 99) >>> arr (\(Stuff a) -> a)+          , input1 = [1 :: Int, 2, 3, 4]+          , input2 = [10 :: Int, 10, 10, 10]+          , output1 = [0, 1, 2, 3]+          , output2 = [99, 10, 10, 10]+          }+    ]
test/Monad.hs view
@@ -13,11 +13,14 @@ -- test-framework-quickcheck2 import Test.Framework.Providers.QuickCheck2 -test = testProperty "State effect" CellMigrationSimulation-  { cell1 = flip runStateC (0 :: Int) $ constM (modify (+ 1))-  , cell2 = flip runStateC 23 $ constM (modify (+ 2))-  , input1 = [(), (), ()]-  , input2 = [(), (), ()]-  , output1 = [((), 1), ((), 2), ((), 3)]-  , output2 = [((), 5), ((), 7), ((), 9)]-  }+test =+  testProperty+    "State effect"+    CellMigrationSimulation+      { cell1 = flip runStateC (0 :: Int) $ constM (modify (+ 1))+      , cell2 = flip runStateC 23 $ constM (modify (+ 2))+      , input1 = [(), (), ()]+      , input2 = [(), (), ()]+      , output1 = [((), 1), ((), 2), ((), 3)]+      , output2 = [((), 5), ((), 7), ((), 9)]+      }
test/Monad/Trans.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-}+ module Monad.Trans where  -- test-framework@@ -13,13 +14,15 @@  -- essence-of-live-coding import LiveCoding-import LiveCoding.Cell.Monad.Trans (State(State))+import LiveCoding.Cell.Monad.Trans (State (State)) -test = testGroup "Monad.Trans"-  [ testProperty "Migrates into runStateL"-    $ \(stateT :: Int) (stateInternal :: Int)-      -> State { .. } === migrate State { stateInternal = 23, .. } stateInternal-  , testProperty "Migrates from runStateL"-    $ \(stateT :: Int) (stateInternal :: Int)-      -> stateInternal === migrate 42 State { .. }-  ]+test =+  testGroup+    "Monad.Trans"+    [ testProperty "Migrates into runStateL" $+        \(stateT :: Int) (stateInternal :: Int) ->+          State {..} === migrate State {stateInternal = 23, ..} stateInternal+    , testProperty "Migrates from runStateL" $+        \(stateT :: Int) (stateInternal :: Int) ->+          stateInternal === migrate 42 State {..}+    ]
test/RuntimeIO/Launch.hs view
@@ -10,14 +10,16 @@ import Test.Framework.Providers.HUnit  -- essence-of-live-coding-import LiveCoding+ import Control.Concurrent (threadDelay)+import LiveCoding  loggingHandle :: IORef [String] -> Handle IO ()-loggingHandle ref = Handle-  { create = modifyIORef ref ("Created handle" :)-  , destroy = const $ modifyIORef ref ("Destroyed handle" :)-  }+loggingHandle ref =+  Handle+    { create = modifyIORef ref ("Created handle" :)+    , destroy = const $ modifyIORef ref ("Destroyed handle" :)+    }  testProgram :: IORef [String] -> LiveProgram (HandlingStateT IO) testProgram ref = liveCell $ handling $ loggingHandle ref
test/TestData/Foo1.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DeriveDataTypeable #-}+ module TestData.Foo1 where  -- base@@ -13,21 +14,22 @@  data Bar   = Bar-  { barA :: Integer-  , barD :: Integer-  , barC :: Bool-  }+      { barA :: Integer+      , barD :: Integer+      , barC :: Bool+      }   | Baar-  { baarB :: Bool-  , baarA :: Int-  }+      { baarB :: Bool+      , baarA :: Int+      }   deriving (Show, Eq, Typeable, Data) -bar = Bar-  { barA = 23-  , barD = 5-  , barC = True-  }+bar =+  Bar+    { barA = 23+    , barD = 5+    , barC = True+    }  data Baz = Baz   { bazFoo :: Foo
test/TestData/Foo2.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE DeriveDataTypeable #-}+ module TestData.Foo2 where  -- base@@ -7,7 +8,7 @@  data Foo   = Fooo Integer-  | Foo  Integer+  | Foo Integer   deriving (Show, Eq, Typeable, Data)  foo = Foo 2@@ -15,26 +16,28 @@  data Bar   = Bar-  { barB :: Integer-  , barA :: Integer-  , barC :: String-  }+      { barB :: Integer+      , barA :: Integer+      , barC :: String+      }   | Baar-  { baarA :: Int-  }+      { baarA :: Int+      }   deriving (Show, Eq, Typeable, Data) -bar = Bar-  { barB = 42-  , barA = 100-  , barC = "Bar"-  }+bar =+  Bar+    { barB = 42+    , barA = 100+    , barC = "Bar"+    } -bar' = Bar-  { barB = 42-  , barA = 23-  , barC = "Bar"-  }+bar' =+  Bar+    { barB = 42+    , barA = 23+    , barC = "Bar"+    }  data Baz = Baz   { bazBar :: Bar@@ -48,5 +51,5 @@ data Frob = Frob Integer   deriving (Show, Eq, Typeable, Data) -frob  = Frob 2+frob = Frob 2 frob' = Frob 1
test/Util.hs view
@@ -1,5 +1,6 @@ {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE RecordWildCards #-}+ module Util where  -- base@@ -37,14 +38,15 @@   }  instance (Eq b, Show b) => Testable (CellMigrationSimulation a b) where-  property CellMigrationSimulation { .. }-    = let Identity (output1', output2') = simulateCellMigration cell1 cell2 input1 input2-      in output1 === output1' .&&. output2 === output2'+  property CellMigrationSimulation {..} =+    let Identity (output1', output2') = simulateCellMigration cell1 cell2 input1 input2+     in output1 === output1' .&&. output2 === output2' --- | Step the first cell with the first input,---   migrate it to the second cell,---   and step the migration result with the second input.---   Return both outputs.+{- | Step the first cell with the first input,+   migrate it to the second cell,+   and step the migration result with the second input.+   Return both outputs.+-} simulateCellMigration :: Monad m => Cell m a b -> Cell m a b -> [a] -> [a] -> m ([b], [b]) simulateCellMigration cell1 cell2 as1 as2 = do   (bs1, cell1') <- steps cell1 as1@@ -64,8 +66,9 @@ inIdentityT :: Cell Identity a prop -> Cell Identity a prop inIdentityT = id --- | Basic unit test for 'Cell's.---   Check whether a given 'input' to your 'cell' results in a given 'output'.+{- | Basic unit test for 'Cell's.+   Check whether a given 'input' to your 'cell' results in a given 'output'.+-} data CellSimulation a b = CellSimulation   { cell :: Cell Identity a b   , input :: [a]@@ -73,11 +76,13 @@   }  instance (Eq b, Show b) => Testable (CellSimulation a b) where-  property CellSimulation { .. } = property CellMigrationSimulation-    { cell1 = cell-    , cell2 = cell-    , input1 = input-    , input2 = []-    , output1 = output-    , output2 = []-    }+  property CellSimulation {..} =+    property+      CellMigrationSimulation+        { cell1 = cell+        , cell2 = cell+        , input1 = input+        , input2 = []+        , output1 = output+        , output2 = []+        }
test/Util/LiveProgramMigration.hs view
@@ -1,9 +1,10 @@-{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE RecordWildCards #-}+ module Util.LiveProgramMigration where  -- transformers-import Control.Monad.Trans.RWS.Strict (runRWS, RWS)+import Control.Monad.Trans.RWS.Strict (RWS, runRWS)  -- QuickCheck import Test.QuickCheck@@ -11,7 +12,8 @@ -- essence-of-live-coding import LiveCoding -data LiveProgramMigration a b = forall s . LiveProgramMigration+data LiveProgramMigration a b = forall s.+  LiveProgramMigration   { liveProgram1 :: LiveProgram (RWS a b s)   , liveProgram2 :: LiveProgram (RWS a b s)   , initialState :: s@@ -28,14 +30,14 @@ stepsLiveProgramRWS liveProg s [] = (liveProg, s, []) stepsLiveProgramRWS liveProg s (a : as) =   let (liveProg', s', b) = stepLiveProgramRWS liveProg a s-  in (liveProg', s', b : third (stepsLiveProgramRWS liveProg' s' as))+   in (liveProg', s', b : third (stepsLiveProgramRWS liveProg' s' as))  third :: (a, b, c) -> c third (a, b, c) = c  instance (Monoid b, Eq b, Show b) => Testable (LiveProgramMigration a b) where-  property LiveProgramMigration { .. } =+  property LiveProgramMigration {..} =     let (liveProg', s', output1') = stepsLiveProgramRWS liveProgram1 initialState input1         liveProg2 = hotCodeSwap liveProgram2 liveProg'         (_, _, output2') = stepsLiveProgramRWS liveProg2 s' input2-    in output1 === output1' .&&. output2 === output2'+     in output1 === output1' .&&. output2 === output2'