essence-of-live-coding (empty) → 0.1.0.0
raw patch · 35 files changed
+3341/−0 lines, 35 filesdep +QuickCheckdep +basedep +essence-of-live-codingsetup-changed
Dependencies added: QuickCheck, base, essence-of-live-coding, syb, test-framework, test-framework-quickcheck2, transformers, vector-sized
Files
- CHANGELOG.md +5/−0
- LICENSE +30/−0
- Setup.hs +2/−0
- app/TestExceptions.hs +28/−0
- essence-of-live-coding.cabal +89/−0
- src/LiveCoding.lhs +30/−0
- src/LiveCoding/Bind.lhs +89/−0
- src/LiveCoding/Cell.lhs +521/−0
- src/LiveCoding/Cell/Feedback.lhs +100/−0
- src/LiveCoding/Cell/HotCodeSwap.hs +17/−0
- src/LiveCoding/Cell/Resample.hs +38/−0
- src/LiveCoding/CellExcept.lhs +107/−0
- src/LiveCoding/Coalgebra.lhs +116/−0
- src/LiveCoding/Debugger.lhs +199/−0
- src/LiveCoding/Debugger/StatePrint.hs +162/−0
- src/LiveCoding/Exceptions.lhs +154/−0
- src/LiveCoding/Exceptions/Finite.lhs +84/−0
- src/LiveCoding/External.hs +54/−0
- src/LiveCoding/Forever.lhs +146/−0
- src/LiveCoding/LiveProgram.lhs +326/−0
- src/LiveCoding/LiveProgram/HotCodeSwap.lhs +26/−0
- src/LiveCoding/Migrate.lhs +91/−0
- src/LiveCoding/Migrate/Debugger.hs +41/−0
- src/LiveCoding/Migrate/Migration.hs +52/−0
- src/LiveCoding/Preliminary/CellExcept.lhs +77/−0
- src/LiveCoding/Preliminary/CellExcept/Applicative.lhs +102/−0
- src/LiveCoding/Preliminary/CellExcept/Monad.lhs +117/−0
- src/LiveCoding/Preliminary/CellExcept/Newtype.lhs +99/−0
- src/LiveCoding/Preliminary/LiveProgram/HotCodeSwap.lhs +62/−0
- src/LiveCoding/Preliminary/LiveProgram/LiveProgram2.lhs +22/−0
- src/LiveCoding/Preliminary/LiveProgram/LiveProgramPreliminary.lhs +48/−0
- src/LiveCoding/RuntimeIO.lhs +178/−0
- test/Main.hs +45/−0
- test/TestData/Foo1.hs +38/−0
- test/TestData/Foo2.hs +46/−0
+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for essence-of-live-coding++## 0.1.0.0 -- YYYY-mm-dd++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright (c) 2018, Manuel Bärenz++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Manuel Bärenz nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ app/TestExceptions.hs view
@@ -0,0 +1,28 @@+{-# LANGUAGE Arrows #-}++-- base+import Control.Arrow++-- transformers+import Control.Monad.Trans.Class++-- essence-of-live-coding+import LiveCoding++liveProgram = liveCell+ $ safely $ do+ try $ throwingCell+ safe $ arr (const (3:: Integer)) >>> sumC >>> arr (const ())++throwingCell = proc _ -> do+ n <- sumC -< (1 :: Integer)+ if n > 10+ then throwC -< ()+ else returnA -< ()+ arrM $ lift . print -< n+++main = do+ (debugger, observer) <- countDebugger+ launchWithDebugger liveProgram $ debugger <> statePrint+ await observer 30
+ essence-of-live-coding.cabal view
@@ -0,0 +1,89 @@+name: essence-of-live-coding+version: 0.1.0.0+synopsis: General purpose live coding framework+description:+ essence-of-live-coding is a general purpose and type safe live coding framework.+ .+ You can run programs in it, and edit, recompile and reload them while they're running.+ Internally, the state of the live program is automatically migrated when performing hot code swap.+ .+ The library also offers an easy to use FRP interface.+ It is parametrized by its side effects,+ separates data flow cleanly from control flow,+ and allows to develop live programs from reusable, modular components.+ There are also useful utilities for debugging and quickchecking.++license: BSD3+license-file: LICENSE+author: Manuel Bärenz+maintainer: programming@manuelbaerenz.de+category: FRP, Live coding+build-type: Simple+extra-source-files: CHANGELOG.md+cabal-version: >=1.10++library+ exposed-modules:+ LiveCoding+ , LiveCoding.Bind+ , LiveCoding.Cell+ , LiveCoding.Cell.Feedback+ , LiveCoding.Cell.HotCodeSwap+ , LiveCoding.Cell.Resample+ , LiveCoding.CellExcept+ , LiveCoding.Coalgebra+ , LiveCoding.Debugger+ , LiveCoding.Debugger.StatePrint+ , LiveCoding.Exceptions+ , LiveCoding.Exceptions.Finite+ , LiveCoding.External+ , LiveCoding.Forever+ , LiveCoding.LiveProgram+ , LiveCoding.LiveProgram.HotCodeSwap+ , LiveCoding.Migrate+ , LiveCoding.Migrate.Migration+ , LiveCoding.Migrate.Debugger+ , LiveCoding.RuntimeIO++ other-modules:+ LiveCoding.Preliminary.CellExcept+ , LiveCoding.Preliminary.CellExcept.Applicative+ , LiveCoding.Preliminary.CellExcept.Monad+ , LiveCoding.Preliminary.CellExcept.Newtype+ , LiveCoding.Preliminary.LiveProgram.HotCodeSwap+ , LiveCoding.Preliminary.LiveProgram.LiveProgram2+ , LiveCoding.Preliminary.LiveProgram.LiveProgramPreliminary++ other-extensions: DeriveDataTypeable+ build-depends:+ base >=4.11 && <4.13+ , transformers == 0.5.*+ , syb == 0.7.*+ , vector-sized == 1.2.*+ hs-source-dirs: src+ default-language: Haskell2010++test-suite essence-of-live-coding+ type: exitcode-stdio-1.0+ main-is: Main.hs+ other-modules:+ TestData.Foo1+ , TestData.Foo2+ hs-source-dirs: test+ build-depends:+ base >=4.11 && <4.13+ , syb == 0.7.*+ , essence-of-live-coding+ , test-framework == 0.8.*+ , test-framework-quickcheck2 == 0.3.*+ , QuickCheck == 2.12.*+ default-language: Haskell2010++executable TestExceptions+ main-is: TestExceptions.hs+ hs-source-dirs: app+ build-depends:+ base >=4.11 && <4.13+ , essence-of-live-coding+ , transformers == 0.5.*+ default-language: Haskell2010
+ src/LiveCoding.lhs view
@@ -0,0 +1,30 @@+\begin{comment}+\begin{code}+module LiveCoding+ (module X)+ where++-- base+import Data.Data as X++-- essence-of-live-coding+import LiveCoding.Bind as X+import LiveCoding.Cell as X+import LiveCoding.Cell.Feedback as X+import LiveCoding.Cell.HotCodeSwap as X+import LiveCoding.Cell.Resample as X+import LiveCoding.CellExcept as X+import LiveCoding.Coalgebra as X+import LiveCoding.Debugger as X+import LiveCoding.Debugger.StatePrint as X+import LiveCoding.Exceptions as X+import LiveCoding.Exceptions.Finite as X+import LiveCoding.Forever as X+import LiveCoding.LiveProgram as X+import LiveCoding.LiveProgram.HotCodeSwap as X+import LiveCoding.Migrate as X+import LiveCoding.Migrate.Debugger as X+import LiveCoding.Migrate.Migration as X+import LiveCoding.RuntimeIO as X+\end{code}+\end{comment}
+ src/LiveCoding/Bind.lhs view
@@ -0,0 +1,89 @@+\begin{comment}+\begin{code}+{-# LANGUAGE Arrows #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+module LiveCoding.Bind where++-- base+import Control.Arrow+import Control.Concurrent (threadDelay)+import Data.Data+import Data.Either (fromRight)+import Data.Void++-- transformers+import Control.Monad.Trans.Class+import Control.Monad.Trans.Except+import Control.Monad.Trans.Reader++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.CellExcept+import LiveCoding.Exceptions+import LiveCoding.LiveProgram+\end{code}+\end{comment}++\begin{comment}+%After this long excursion,+We can finally return to the example.+Let us again change the period of the oscillator,+only this time not manually,+but at the moment the position reaches 0:++\begin{code}+throwWhen0+ :: Monad m+ => Cell (ExceptT () m) Double Double+throwWhen0 = proc pos ->+ if pos < 0+ then throwC -< ()+ else returnA -< pos++sineChangeE = do+ try $ sine 6 >>> throwWhen0+ try $ (constM $ lift $ putStrLn "I changed!")+ >>> throwC+ safe $ sine 10+\end{code}+\end{comment}++\begin{code}+sineWait+ :: Double+ -> CellExcept IO () String Void+sineWait t = do+ try $ arr (const "Waiting...")+ >>> wait 2+ safe $ sine t+ >>> arr asciiArt+\end{code}+This \mintinline{haskell}{do}-block can be read intuitively.+Initially, the first cell is executed,+which returns the message \mintinline{haskell}{"Waiting..."} every second.+After three seconds, it throws an exception,+which is handled by activating the sine generator.+Since all exceptions have been handled,+we leave the \mintinline{haskell}{CellExcept} context and run the resulting program:+\begin{code}+printSineWait :: LiveProgram IO+printSineWait = liveCell+ $ safely (sineWait 10)+ >>> printEverySecond+\end{code}+\verbatiminput{../demos/DemoSineWait.txt}+The crucial advantage of handling control flow this way+is that the \emph{control state}+-- that is, the information which exceptions have been thrown and which cell is currently active --+is encoded completely in the overall state of the live program,+and can thus be migrated automatically.+Let us rerun the above example,+but after the first \mintinline{haskell}{try} statement has already passed control to the sine generator+we shorten the period length of the sine wave and reload:+\verbatiminput{../demos/DemoSineWaitChange.txt}+The migrated program did not restart and wait again,+but remembered to immediately continue executing the sine generator from the same phase as before.+This is in contrast to simplistic approaches to live coding in which the control flow state is forgotten upon reload,+and restarted each time.
+ src/LiveCoding/Cell.lhs view
@@ -0,0 +1,521 @@+\fxerror{If more space left, show definitions and explain}+\fxerror{Reorganise in modules properly. For now, don't worry too much.}+\begin{comment}+\begin{code}+-- | TODO: Proper haddock docs+{-# LANGUAGE Arrows #-}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RecursiveDo #-}+{-# LANGUAGE TupleSections #-}+module LiveCoding.Cell where++-- base+import Control.Arrow+import Control.Category+import Control.Concurrent (threadDelay)+import Control.Monad ((>=>)) -- Only for rewrite rule+import Control.Monad.Fix+import Data.Data+import Prelude hiding ((.), id)++-- transformers+import Control.Monad.Trans.Class+import Control.Monad.Trans.Reader++-- essence-of-live-coding+import LiveCoding.LiveProgram++\end{code}+\end{comment}+\fxerror{+Is it clear that we do this FRP approach because of modularity,+both in the program definitions and also in the state types?+Maybe don't show the definitions of the primitives, but show the state types,+and the custom migrations implemented so that FRP reloads correctly.+Ideally, show the custom migrations as examples how users can add their own migrations.+The main connective could be that Cells build up their state automatically in a way that the migration works well.+(Test)+}+\fxerror{An important point along those lines would also be that the state type becomes a tree,+branching at \mintinline{haskell}{>>>} and \mintinline{haskell}{***} and \mintinline{haskell}{+++},+so individual subtrees are preserved well+}+In ordinary functional programming, the smallest building blocks are functions.+It stands to reason that in live coding, they should also be some flavour of functions,+in fact, \mintinline{haskell}{Arrow}s \cite{Arrows}.+We will see that it is possible to define bigger live programs from reusable components.+Crucially, the library user is disburdened from separating state and step function.+The state type is built up behind the scenes,+in a manner compatible with the automatic state migration.++\subsection{Cells}+\label{sec:cells}++In our definition of live programs as pairs of state and state steppers,+we can generalise the step functions to an additional input and output type.+\begin{comment}+\begin{spec}+mStep :: a -> s -> m (b, s)+\end{spec}+By now, the reader may have rightfully become weary of the ubiquitous \mintinline{haskell}{IO} monad;+and promoting it to an arbitrary monad will turn out shortly to be a very useful generalisation.+\fxerror{This has now been introduced earlier, in the WAI example, as Reader.}++We collect these insights in a definition,+\end{comment}+Live programs are thus generalised to effectful \emph{Mealy machines} \cite{Mealy}.+Let us call them cells, the building blocks of everything live:+\begin{comment}+\begin{code}+-- | The basic building block of a live program.+\end{code}+\end{comment}+\begin{code}+data Cell m a b = forall s . Data s => Cell+ { cellState :: s+ , cellStep :: s -> a -> m (b, s)+ }+\end{code}+Such a cell may progress by one step,+consuming an \mintinline{haskell}{a} as input,+and producing, by means of an effect in some monad \mintinline{haskell}{m},+not only the updated cell,+but also an output datum \mintinline{haskell}{b}:++\begin{code}+step+ :: Monad m+ => Cell m a b+ -> a -> m (b, Cell m a b)+step Cell { .. } a = do+ (b, cellState') <- cellStep cellState a+ return (b, Cell { cellState = cellState', .. })+\end{code}++\begin{comment}+\begin{code}+steps+ :: Monad m+ => Cell m a b+ -> [a]+ -> m ([b], Cell m a b)+steps cell [] = return ([], cell)+steps cell (a : as) = do+ (b, cell') <- step cell a+ (bs, cell'') <- steps cell' as+ return (b : bs, cell'')+\end{code}+\end{comment}++As a simple example, consider the following \mintinline{haskell}{Cell} which adds all input and returns the delayed sum each step:+\begin{code}+sumC :: (Monad m, Num a, Data a) => Cell m a a+sumC = Cell { .. }+ where+ cellState = 0+ cellStep accum a = return (accum, accum + a)+\end{code}++We recover live programs as the special case of trivial input and output:+\begin{code}+liveCell+ :: Functor m+ => Cell m () ()+ -> LiveProgram m+liveCell Cell { .. } = LiveProgram+ { liveState = cellState+ , liveStep = fmap snd . flip cellStep ()+ }+\end{code}+\begin{comment}+\begin{code}+toLiveCell+ :: Functor m+ => LiveProgram m+ -> Cell m () ()+toLiveCell LiveProgram { .. } = Cell+ { cellState = liveState+ , cellStep = \s () -> ((), ) <$> liveStep s+ }+\end{code}+\end{comment}++\subsection{FRP for automata-based programming}+Effectful Mealy machines, here cells,+offer a wide variety of applications in FRP.+The essential parts of the API,+which is heavily inspired by the FRP library \texttt{dunai}+\cite{Dunai},+is shown here.+%\mintinline{haskell}{Cell}s can be composed in three directions:+%Sequentially and parallely in the data flow sense,+%and sequentially in the control flow sense.+We will address the data flow aspects in this section,+investigating control flow later in Section \ref{sec:control flow}.++\begin{comment}+\begin{code}+hoistCell morph Cell { .. } = Cell+ { cellStep = \s a -> morph $ cellStep s a+ , ..+ }+\end{code}+\end{comment}++\paragraph{Composition}+By being an instance of the type class \mintinline{haskell}{Category}+for any monad \mintinline{haskell}{m},+cells implement sequential composition:+\begin{spec}+(>>>)+ :: Monad m+ => Cell m a b+ -> Cell m b c+ -> Cell m a c+\end{spec}++\begin{comment}+\begin{code}+-- TODO For some weird reason, this is more efficient than my own ADT+newtype Composition state1 state2 = Composition (state1, state2)+ deriving Data++getState2 :: Composition state1 state2 -> state2+getState2 (Composition (state1, state2)) = state2++instance Monad m => Category (Cell m) where+ id = Cell+ { cellState = ()+ , cellStep = \() a -> return (a, ())+ }++ Cell state2 step2 . Cell state1 step1 = Cell { .. }+ where+ cellState = Composition (state1, state2)+ cellStep (Composition (state1, state2)) a = do+ (b, state1') <- step1 state1 a+ (!c, state2') <- step2 state2 b+ return (c, Composition (state1', state2'))+-- {-# RULES+-- "arrM/>>>" forall (f :: forall a b m . Monad m => a -> m b) g . arrM f >>> arrM g = arrM (f >=> g)+-- #-} -- Don't really need rules here because GHC will inline all that anyways+\end{code}+\end{comment}+For two cells \mintinline{haskell}{cell1} and \mintinline{haskell}{cell2}+with state types \mintinline{haskell}{state1} and \mintinline{haskell}{state2},+the composite \mintinline{haskell}{cell1 >>> cell2} holds a pair of both states:+\fxwarning{Syntax highlighting is not very good here}+\begin{spec}+data Composition state1 state2 = Composition+ { state1 :: state1+ , state2 :: state2+ } deriving Data+\end{spec}+The step function executes the steps of both cells after each other.+They only touch their individual state variable,+the state stays encapsulated.++\fxwarning{Reuse Sensor, SF and Actuator later?}+Composing \mintinline{haskell}{Cell}s sequentially allows us to form live programs out of \emph{sensors}, pure signal functions and \emph{actuators}:+\begin{code}+type Sensor a = Cell IO () a+type SF a b = forall m . Cell m a b+type Actuator b = Cell IO b ()+\end{code}+\begin{code}+buildLiveProg+ :: Sensor a+ -> SF a b+ -> Actuator b+ -> LiveProgram IO+buildLiveProg sensor sf actuator = liveCell+ $ sensor >>> sf >>> actuator+\end{code}+This will conveniently allow us to build a whole live program from smaller components.+It is never necessary to specify a big state type manually,+it will be composed from basic building blocks like \mintinline{haskell}{Composition}.++\paragraph{Arrowized FRP}+\mintinline{haskell}{Cell}s can be made an instance of the \mintinline{haskell}{Arrow} type class,+which allows us to lift pure functions to \mintinline{haskell}{Cell}s:+\begin{spec}+arr+ :: Monad m+ -> (a -> b)+ -> Cell m a b+\end{spec}+\fxwarning{Would be nice to have the space to explain *** as well!}+Together with the \mintinline{haskell}{ArrowChoice} and \mintinline{haskell}{ArrowLoop} classes+(discussed in the appendix),+cells can be used in \emph{arrow notation} \cite{ArrowNotation} with \mintinline{haskell}{case}-expressions,+\mintinline{haskell}{if then else} constructs and recursion.+The next subsection gives some examples.++An essential aspect of an FRP framework is some notion of \emph{time}.+\fxwarning{Citation?}+As this approach essentially uses the \texttt{dunai} API,+a detailed treatment of time domains and clocks as in \cite{Rhine} can be readily applied here.+But let us, for simplicity and explicitness,+assume that we will execute all \mintinline{haskell}{Cell}s at a certain fixed step rate,+say a thousand steps per second.+Then an Euler integration cell can be defined:+\begin{code}+stepRate :: Num a => a+stepRate = 25+\end{code}+\begin{code}+integrate+ :: (Data a, Fractional a, Monad m)+ => Cell m a a+integrate = arr (/ stepRate) >>> sumC+\end{code}+The time since activation of a cell is then famously \cite[Section 2.4]{Yampa} defined as:+\begin{code}+localTime+ :: (Data a, Fractional a, Monad m)+ => Cell m b a+localTime = arr (const 1) >>> integrate+\end{code}++\fxwarning{I cut a more detailed discussion about ArrowChoice and ArrowLoop here. Put in the appendix?}++\paragraph{Monads and their morphisms}+Beyond standard arrows, a \mintinline{haskell}{Cell} can encode effects in a monad,+so it is not surprising that Kleisli arrows can be lifted:+\begin{spec}+arrM+ :: Monad m+ -> (a -> m b)+ -> Cell m a b+\end{spec}+\begin{comment}+Mere monadic actions become a special case thereof:+\begin{spec}+constM+ :: Monad m+ -> m b+ -> Cell m a b+\end{spec}+\end{comment}++In case our \mintinline{haskell}{Cell} is in another monad than \mintinline{haskell}{IO},+one can define a function that transports a cell along a monad morphism:+\begin{code}+hoistCell+ :: (forall x . m1 x -> m2 x)+ -> Cell m1 a b -> Cell m2 a b+\end{code}+For example, we may eliminate a \mintinline{haskell}{ReaderT r} context by supplying the environment through the \mintinline{haskell}{runReaderT} monad morphism,+or lift into a monad transformer:+\begin{comment}+\begin{code}+runReaderC+ :: r+ -> Cell (ReaderT r m) a b+ -> Cell m a b+runReaderC r = hoistCell $ flip runReaderT r+\end{code}+\end{comment}+\begin{code}+liftCell+ :: (Monad m, MonadTrans t)+ => Cell m a b+ -> Cell (t m) a b+liftCell = hoistCell lift+\end{code}+As described in \cite[Section 4]{Dunai},+we can successively handle effects+(such as global state, read-only variables, logging, exceptions, and others)+until we arrive at \mintinline{haskell}{IO}.+Then we can execute the live program in the same way as before.++\fxerror{Talk about this more general transformation in the comments?}+\begin{comment}+\begin{code}+transformOutput+ :: (Monad m1, Monad m2)+ => (forall s . m1 (b1, s) -> m2 (b2, s))+ -> Cell m1 a b1+ -> Cell m2 a b2+transformOutput morph Cell { .. } = Cell+ { cellState = cellState+ , cellStep = (morph .) . cellStep+ }++--data Parallel s1 s2 = Parallel s1 s2+newtype Parallel s1 s2 = Parallel (s1, s2)+ deriving Data++instance Monad m => Arrow (Cell m) where+ arr f = Cell+ { cellState = ()+ , cellStep = \() a -> return (f a, ())+ }++ Cell state1 step1 *** Cell state2 step2 = Cell { .. }+ where+ cellState = Parallel (state1, state2)+ cellStep (Parallel (state1, state2)) (a, c) = do+ (b, state1') <- step1 state1 a+ (d, state2') <- step2 state2 c+ return ((b, d), Parallel (state1', state2'))++arrM :: Functor m => (a -> m b) -> Cell m a b+arrM f = Cell+ { cellState = ()+ , cellStep = \() a -> (, ()) <$> f a+ }++constM :: Functor m => m b -> Cell m a b+constM = arrM . const+\end{code}+\end{comment}++\begin{comment}+\begin{code}+instance MonadFix m => ArrowLoop (Cell m) where+ loop (Cell state step) = Cell { .. }+ where+ cellState = state+ cellStep state a = do+ rec ((b, c), state') <- (\c' -> step state (a, c')) c+ return (b, state')++{-+instance ArrowLoop (Cell Identity) where+ loop (Cell state step) = Cell { .. }+ where+ cellState = state+ changedStep state (a, c) = runIdentity $ step state (a, c)+ cellStep state a = let ((b, c), state') = (\c' -> changedStep state (a, c')) c+ in return (b, state')+-}+\end{code}+\end{comment}++\subsection{A sine generator}+Making use of the \mintinline{haskell}{Arrows} syntax extension,+we can implement a harmonic oscillator that will produce a sine wave with amplitude 10 and given period length:+\fxwarning{Comment on rec and ArrowFix}+\fxerror{I want to add a delay for numerical stability}+\begin{code}+sine+ :: MonadFix m+ => Double -> Cell m () Double+sine t = proc () -> do+ rec+ let acc = - (2 * pi / t) ^ 2 * (pos - 10)+ vel <- integrate -< acc+ pos <- integrate -< vel+ returnA -< pos+\end{code}+By the laws of physics, velocity is the integral of accelleration,+and position is the integral of velocity.+In a harmonic oscillator, the acceleration is in the negative direction of the position,+multiplied by a spring factor depending on the period length,+which can be given as an argument.+The integration arrow encapsulates the current position and velocity of the oscillator as internal state, and returns the position.++The sine generator could in principle be used in an audio or video application.+For simplicity, we choose to visualise the signal on the console instead,+with our favourite Haskell operator varying its horizontal position:+\begin{code}+asciiArt :: Double -> String+asciiArt n = replicate (round n) ' ' ++ ">>="+\end{code}+\begin{code}+printEverySecond :: Cell IO String ()+printEverySecond = proc string -> do+ count <- sumC -< 1 :: Integer+ if count `mod` stepRate == 0+ then arrM putStrLn -< string+ else returnA -< ()+\end{code}+Our first live program+written in FRP is ready:+\begin{code}+printSine :: Double -> LiveProgram IO+printSine t = liveCell+ $ sine t+ >>> arr asciiArt+ >>> printEverySecond+\end{code}+\fxwarning{Maybe mention that we could use this in gloss, audio or whatever?}++What if we would run it,+and change the period in mid-execution?+%This is exactly what the framework was designed for.+\fxerror{Show Demo.hs as soon as I've explained the runtime in the previous section}+We execute the program such that after a certain time,+the live environment inserts \mintinline{haskell}{printSine} with a different period.+\fxerror{Actually, now that we have those fancy GHCi commands,+We can insert them instead of manually printing stuff.+Increases the immersion.+But it's actually cheating.+}+Let us execute it:\footnote{%+From now on, the GHCi commands will be suppressed.+}+\verbatiminput{../demos/DemoSine.txt}+It is clearly visible how the period of the oscillator changed,+\fxwarning{Only if this doesn't break. Maybe make figures?}+while its position (or, in terms of signal processing, its phase)+did not jump.+If we use the oscillator in an audio application,+we can retune it without hearing a glitch;+if we use it in a video application,+the widget will smoothly change its oscillating velocity without a jolt.++\section{Control flow}+\label{sec:control flow}+\fxerror{Show only stuff where I can show most of the implementation. Reimplement, in a separate file, the API for the newtype, show its code and explain it.}+Although we now have the tools to build big signal pathways from single cells,+we have no way yet to let the incoming data decide which of several offered pathways to take for the rest of the execution.+While we can (due to \mintinline{haskell}{ArrowChoice}) temporarily branch between two cells using \mintinline{haskell}{if then else},+the branching is reevaluated (and the previous choice forgotten) every step.+We are lacking permanent \emph{control flow}.++The primeval arrowized FRP framework Yampa \cite{Yampa} caters for this requirement by means of switching from a signal function to another if an event occurs.+\fxwarning{Possibly I've mentioned both earlier}+Dunai \cite[Section 5.3]{Dunai}, taking the monadic aspect seriously,+\fxwarning{Dunai, Yampa -> \texttt{Dunai} etc.?}+rediscovers switching as effect handling in the \mintinline{haskell}{Either} monad.+\begin{comment}+We shall see that,+although the state of a \mintinline{haskell}{Cell} is strongly restricted by the \mintinline{haskell}{Data} type class,+we can get very close to this powerful approach to control flow.+\end{comment}++\begin{comment}+\begin{code}+-- FIXME Why the hell is my left definition wrong or leads to the wrong instance?+data Choice stateL stateR = Choice+ { choiceLeft :: stateL+ , choiceRight :: stateR+ }+ deriving Data+instance Monad m => ArrowChoice (Cell m) where+{-+ left (Cell state step) = Cell { cellState = state, .. }+ where+ cellStep cellState (Left a) = do+ (b, cellState') <- step state a+ return (Left b, cellState')+ cellStep cellState (Right b) = return (Right b, cellState)+ -}+ (Cell stateL stepL) +++ (Cell stateR stepR) = Cell { .. }+ where+ cellState = Choice stateL stateR+ cellStep (Choice stateL stateR) (Left a) = do+ (b, stateL') <- stepL stateL a+ return (Left b, (Choice stateL' stateR))+ cellStep (Choice stateL stateR) (Right c) = do+ (d, stateR') <- stepR stateR c+ return (Right d, (Choice stateL stateR'))+\end{code}+\end{comment}
+ src/LiveCoding/Cell/Feedback.lhs view
@@ -0,0 +1,100 @@+\begin{comment}+\begin{code}+{-# LANGUAGE Arrows #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RecordWildCards #-}++module LiveCoding.Cell.Feedback where++-- base+import Control.Arrow+import Data.Data+import Data.Maybe (fromMaybe)++-- essence-of-live-coding+import LiveCoding.Cell+\end{code}+\end{comment}++We would like to have all basic primitives needed to develop standard synchronous signal processing components,+without touching the \mintinline{haskell}{Cell} constructor anymore.+One crucial bit is missing to achieve this goal:+Encapsulating state.+The most general such construction is the feedback loop:+\begin{code}+feedback+ :: (Monad m, Data s)+ => s+ -> Cell m (a, s) (b, s)+ -> Cell m a b+\end{code}+Let us have a look at its internal state:+\begin{spec}+data Feedback sPrevious sAdditional = Feedback+ { sPrevious :: sPrevious+ , sAdditional :: sAdditional+ }+\end{spec}+In \mintinline{haskell}{feedback sAdditional cell},+the \mintinline{haskell}{cell} has state \mintinline{haskell}{sPrevious},+and to this state we add \mintinline{haskell}{sAdditional}.+The additional state is received by \mintinline{haskell}{cell} as explicit input,+and \mintinline{haskell}{feedback} hides it.++Note that \mintinline{haskell}{feedback} and \mintinline{haskell}{loop} are different.+While \mintinline{haskell}{loop} provides immediate recursion, it doesn't add new state.+\mintinline{haskell}{feedback} requires an initial state and delays it,+but in turn it is always safe to use since it does not use \mintinline{haskell}{mfix}.++\fxwarning{Possibly remark on Data instance of s?}+\begin{comment}+\begin{code}+newtype Feedback s s' = Feedback (s, s')+ deriving Data++feedback s (Cell state step) = Cell { .. }+ where+ cellState = Feedback (state, s)+ cellStep (Feedback (state, s)) a = do+ ((b, s'), state') <- step state (a, s)+ return (b, Feedback (state', s'))+\end{code}+\end{comment}+It enables us to write delays:+\begin{code}+delay :: (Data s, Monad m) => s -> Cell m s s+delay s = feedback s $ arr swap+ where+ swap (sNew, sOld) = (sOld, sNew)+\end{code}+\mintinline{haskell}{feedback} can be used for accumulation of data.+For example, \mintinline{haskell}{sumC} now becomes:+\begin{code}+sumFeedback+ :: (Monad m, Num a, Data a)+ => Cell m a a+sumFeedback = feedback 0 $ arr+ $ \(a, accum) -> (accum, a + accum)+\end{code}++\fxerror{Mention keepJust and keep}+\begin{comment}+\begin{code}+keepJust+ :: (Monad m, Data a)+ => Cell m (Maybe a) (Maybe a)+keepJust = feedback Nothing $ arr keep+ where+ keep (Nothing, Nothing) = (Nothing, Nothing)+ keep (_, Just a) = (Just a, Just a)+ keep (Just a, Nothing) = (Just a, Just a)++-- | Initialise with a value 'a'.+-- If the input is 'Nothing', @keep a@ will output the stored indefinitely.+-- A new value can be stored by inputting 'Maybe a'.+keep :: (Data a, Monad m) => a -> Cell m (Maybe a) a+keep a = feedback a $ proc (ma, aOld) -> do+ let aNew = fromMaybe aOld ma+ returnA -< (aNew, aNew)+\end{code}+\end{comment}
+ src/LiveCoding/Cell/HotCodeSwap.hs view
@@ -0,0 +1,17 @@+module LiveCoding.Cell.HotCodeSwap where++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Migrate++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+ }
+ src/LiveCoding/Cell/Resample.hs view
@@ -0,0 +1,38 @@+{- |+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 NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+module LiveCoding.Cell.Resample where++-- base+import Control.Arrow+import Data.Maybe+import GHC.TypeNats++-- vector-sized+import Data.Vector.Sized++-- essence-of-live-coding+import LiveCoding.Cell++-- | Execute the inner cell for n steps per outer step.+resample :: (Monad m, KnownNat n) => Cell m a b -> Cell m (Vector n a) (Vector n b)+resample cell = arr toList >>> resampleList cell >>> arr (fromList >>> fromJust)++-- | Execute the cell for as many steps as the input list is long.+resampleList :: Monad m => Cell m a b -> Cell m [a] [b]+resampleList Cell { cellState, cellStep = singleStep } = Cell { .. }+ where+ cellStep s [] = return ([], s)+ cellStep s (a : as) = do+ (b , s' ) <- singleStep s a+ (bs, s'') <- cellStep 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
+ src/LiveCoding/CellExcept.lhs view
@@ -0,0 +1,107 @@+\begin{comment}+\begin{code}+{-# LANGUAGE GADTs #-}++module LiveCoding.CellExcept where++-- base+import Control.Monad+import Data.Data+import Data.Void++-- transformers+import Control.Monad.Trans.Except++-- essenceoflivecoding+import LiveCoding.Cell+import LiveCoding.Exceptions+import LiveCoding.Exceptions.Finite+\end{code}+\end{comment}++We can save on boiler plate by dropping the Coyoneda embedding for an ``operational'' monad:+\fxerror{Cite operational}+\fxerror{Move the following code into appendix?}+\begin{code}+data CellExcept m a b e where+ Return :: e -> CellExcept m a b e+ Bind+ :: CellExcept m a b e1+ -> (e1 -> CellExcept m a b e2)+ -> CellExcept m a b e2+ Try+ :: (Data e, Finite e)+ => Cell (ExceptT e m) a b+ -> CellExcept m a b e+\end{code}++\begin{comment}+\begin{code}+instance Monad m => Functor (CellExcept m a b) where+ fmap = liftM++instance Monad m => Applicative (CellExcept m a b) where+ pure = return+ (<*>) = ap+\end{code}+\end{comment}+The \mintinline{haskell}{Monad} instance is now trivial:+\begin{code}+instance Monad m => Monad (CellExcept m a b) where+ return = Return+ (>>=) = Bind+\end{code}+As is typical for operational monads, all of the effort now goes into the interpretation function:+\begin{code}+runCellExcept+ :: Monad m+ => CellExcept m a b e+ -> Cell (ExceptT e m) a b+\end{code}+\begin{spec}+runCellExcept (Bind (Try cell) g)+ = cell >>>= commute (runCellExcept . g)+runCellExcept ... = ...+\end{spec}+\begin{comment}+\begin{code}+runCellExcept (Return e) = constM $ throwE e+runCellExcept (Try cell) = cell+runCellExcept (Bind (Try cell) g) = cell >>>== commute (runCellExcept . g)+runCellExcept (Bind (Return e) f) = runCellExcept $ f e+runCellExcept (Bind (Bind ce f) g) = runCellExcept $ Bind ce $ \e -> Bind (f e) g+\end{code}+\end{comment}++As a slight restriction of the framework,+throwing exceptions is now only allowed for finite types:+\begin{code}+try+ :: (Data e, Finite e)+ => Cell (ExceptT e m) a b+ -> CellExcept m a b e+try = Try+\end{code}+In practice however, this is less often a limitation than first assumed,+since in the monad context,+calculations with all types are allowed again.+\fxerror{But the trouble remains that builtin types like Int and Double can't be thrown.}++\fxfatal{The rest is explained in the main article differently. Merge.}+\begin{comment}+\begin{code}+safely+ :: Monad m+ => CellExcept m a b Void+ -> Cell m a b+safely = hoistCell discardVoid . runCellExcept+discardVoid+ :: Functor m+ => ExceptT Void m a+ -> m a+discardVoid+ = fmap (either absurd id) . runExceptT+safe :: Monad m => Cell m a b -> CellExcept m a b Void+safe cell = try $ liftCell cell+\end{code}+\end{comment}
+ src/LiveCoding/Coalgebra.lhs view
@@ -0,0 +1,116 @@+\begin{comment}+\begin{code}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RecordWildCards #-}++module LiveCoding.Coalgebra where++-- base+import Control.Arrow (second)+import Data.Data++-- essence-of-live-coding+import LiveCoding.Cell++\end{code}+\end{comment}++\section{Monadic stream functions and final coalgebras}++\label{sec:msfs and final coalgebras}++\mintinline{haskell}{Cell}s mimick Dunai's \cite{Dunai} monadic stream functions (\mintinline{haskell}{MSF}s) closely.+But can they fill their footsteps completely in terms of expressiveness?+If not, which programs exactly can be represented as \mintinline{haskell}{MSF}s and which can't?+To find the answer to these questions,+let us reexamine both types.++With the help of a simple type synonym,+the \mintinline{haskell}{MSF} definition can be recast in explicit fixpoint form:++\begin{code}+type StateTransition m a b s = a -> m (b, s)++data MSF m a b = MSF+ { unMSF :: StateTransition m a b (MSF m a b)+ }+\end{code}+This definition tells us that monadic stream functions are so-called \emph{final coalgebras} of the \mintinline{haskell}{StateTransition} functor+(for fixed \mintinline{haskell}{m}, \mintinline{haskell}{a}, and \mintinline{haskell}{b}).+An ordinary coalgebra for this functor is given by some type \mintinline{haskell}{s} and a coalgebra structure map:+\begin{code}+data Coalg m a b where+ Coalg+ :: s+ -> (s -> StateTransition m a b s)+ -> Coalg m a b+\end{code}+But hold on, the astute reader will intercept,+is this not simply the definition of \mintinline{haskell}{Cell}?+Alas, it is not, for it lacks the type class restriction \mintinline{haskell}{Data s},+which we need so dearly for the type migration.+Any cell is a coalgebra,+but only those coalgebras that satisfy this type class are a cell.++Oh, if only there were no such distinction.+By the very property of the final coalgebra,+we can embed every coalgebra therein:+\begin{code}+finality :: Monad m => Coalg m a b -> MSF m a b+finality (Coalg state step) = MSF $ \a -> do+ (b, state') <- step state a+ return (b, finality $ Coalg state' step)+\end{code}+And analogously, every cell can be easily made into an \mintinline{haskell}{MSF} without loss of information:+\begin{code}+finalityC :: Monad m => Cell m a b -> MSF m a b+finalityC Cell { .. } = MSF $ \a -> do+ (b, cellState') <- cellStep cellState a+ return (b, finalityC $ Cell cellState' cellStep)+\end{code}+And the final coalgebra is of course a mere coalgebra itself:+\begin{code}+coalgebra :: MSF m a b -> Coalg m a b+coalgebra msf = Coalg msf unMSF+\end{code}+But we miss the abilty to encode \mintinline{haskell}{MSF}s as \mintinline{haskell}{Cell}s by just the \mintinline{haskell}{Data} type class:+\begin{code}+coalgebraC+ :: Data (MSF m a b)+ => MSF m a b+ -> Cell m a b+coalgebraC msf = Cell msf unMSF+\end{code}+We are out of luck if we would want to derive an instance of \mintinline{haskell}{Data (MSF m a b)}.+Monadic stream functions are, well, functions,+and therefore have no \mintinline{haskell}{Data} instance.+The price of \mintinline{haskell}{Data} is loss of higher-order state.+Just how big this loss is will be demonstrated in the following section.++\begin{comment}+\subsection{Initial algebras}++\begin{code}+type AlgStructure m a b s = StateTransition m a b s -> s+data Alg m a b where+ Alg+ :: s+ -> AlgStructure m a b s+ -> Alg m a b++algMSF :: MSF m a b -> Alg m a b+algMSF msf = Alg msf MSF++-- TODO Could explain better why this is simpler in the coalgebra case+initiality+ :: Functor m+ => AlgStructure m a b s+ -> MSF m a b+ -> s+initiality algStructure = go+ where+ go msf = algStructure $ \a -> second go <$> unMSF msf a++\end{code}+\end{comment}
+ src/LiveCoding/Debugger.lhs view
@@ -0,0 +1,199 @@+\begin{comment}+\begin{code}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StandaloneDeriving #-}++module LiveCoding.Debugger where++-- base+import Control.Concurrent+import Control.Monad (void)+import Data.Data+import Data.IORef++-- transformers+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.State++-- syb+import Data.Generics.Text++-- essence-of-live-coding+import LiveCoding.LiveProgram+import LiveCoding.Cell++\end{code}+\end{comment}++\subsection{Debugging the live state}+Having the complete state of the program in one place allows us to inspect and debug it in a central place.+We might want to interact with the user,+display aspects of the state+and possibly even change it in place.+In short, a debugger is a program that can read and modify,+as an additional effect,+the state of an arbitrary live program:+\begin{code}+newtype Debugger m = Debugger+ { getDebugger :: forall s .+ Data s => LiveProgram (StateT s m)+ }+\end{code}+\begin{comment}+\begin{code}+-- Standalone deriving isn't clever enough to handle the existential type+instance Monad m => Semigroup (Debugger m) where+ debugger1 <> debugger2 = Debugger $ getDebugger debugger1 <> getDebugger debugger2++instance Monad m => Monoid (Debugger m) where+ mempty = Debugger mempty++getC :: Monad m => Cell (StateT s m) a s+getC = constM get++putC :: Monad m => Cell (StateT s m) s ()+putC = arrM put+\end{code}+\end{comment}+A simple debugger prints the unmodified state to the console:+\begin{code}+gshowDebugger :: Debugger IO+gshowDebugger = Debugger+ $ liveCell $ arrM $ const $ do+ state <- get+ lift $ putStrLn $ gshow state+\end{code}+Thanks to the \mintinline{haskell}{Data} typeclass,+the state does not need to be an instance of \mintinline{haskell}{Show} for this to work:+\texttt{syb} offers a generic \mintinline{haskell}{gshow} function.+A more sophisticated debugger could connect to a GUI and display the state there,+even offering the user to pause the execution and edit the state live.+\fxwarning{Should I explain countDebugger? What for?}+\fxerror{Following a comment on cat theory. Add to appendix?}+\begin{comment}+Debuggers are endomorphisms in the Kleisli category of \mintinline{haskell}{IO},+and thus \mintinline{haskell}{Monoid}s:+A pair of them can be chained by executing them sequentially,+and the trivial debugger purely \mintinline{haskell}{return}s the state unchanged.+\end{comment}+We can bake a debugger into a live program:+\begin{code}+withDebugger+ :: Monad m+ => LiveProgram m+ -> Debugger m+ -> LiveProgram m+\end{code}+\begin{comment}+\begin{code}+withDebugger = (liveCell .) . withDebuggerC . toLiveCell++withDebuggerC+ :: Monad m+ => Cell m a b+ -> Debugger m+ -> Cell m a b+withDebuggerC (Cell state step) (Debugger (LiveProgram dbgState dbgStep)) = Cell { .. }+ where+ cellState = Debugging { .. }+ cellStep Debugging { .. } a = do+ (b, state') <- step state a+ states <- runStateT (dbgStep dbgState) state'+ return (b, uncurry (flip Debugging) states)+\end{code}+\end{comment}+Again, let us understand the function through its state type:+\begin{code}+data Debugging dbgState state = Debugging+ { state :: state+ , dbgState :: dbgState+ } deriving (Data, Eq, Show)+\end{code}+On every step, the debugger becomes active after the cell steps,+and is fed the current \mintinline{haskell}{state} of the main program.+Depending on \mintinline{haskell}{dbgState},+it may execute some side effects or mutate the \mintinline{haskell}{state},+or do nothing at all\footnote{%+This option is important for performance: E.g. for an audio application,+a side effect on every sample can slow down unbearably.}.++Live programs with debuggers are started just as usual.+\begin{comment}+\fxwarning{Automatise this and the next output}+Inspecting the state of the example \mintinline{haskell}{printSineWait} from Section \ref{sec:control flow context} is daunting, though:+\begin{verbatim}+Waiting...+(Composition ((,) (Composition ((,) (()) +(Composition ((,) (()) (Composition ((,) +(Composition ((,) (()) (Composition ((,) +[...]+\end{verbatim}+\fxerror{I still have the tuples here!}+The arrow syntax desugaring introduces a lot of irrelevant overhead such as compositions with the trivial state type \mintinline{haskell}{()},+hiding the parts of the state we are actually interested in.+Luckily, it is a simple, albeit lengthy exercise in generic programming to prune all irrelevant parts of the state,+resulting in a tidy output%\footnote{%+%Line breaks were added to fit the columns.}+ like:+\end{comment}+Let us inspect the state of the example \mintinline{haskell}{printSineWait} from Section \ref{sec:control flow context}.+It is a simple, albeit lengthy exercise in generic programming to prune all irrelevant parts of the state when printing it,+resulting in a tidy output like:+\fxwarning{Automatise this}+\begin{verbatim}+Waiting...+NotThrown: (1.0e-3)+ >>> +(0.0) >>> (0.0)+ >>> (1)+NotThrown: (2.0e-3)+ >>> +(0.0) >>> (0.0)+ >>> (2)+[...]+Waiting...+NotThrown: (2.0009999999998906)+ >>> +(0.0) >>> (0.0)+ >>> (2001)+Exception:+ >>> +(3.9478417604357436e-3) >>> (0.0)++ >>> (2002)+[...]+\end{verbatim}+\begin{comment}+Exception:+ >>> +(7.895683520871487e-3) >>>+ (3.947841760435744e-6)++ >>> (2003)+\end{comment}+The cell is initialised in a state where the exception hasn't been thrown yet,+and the \mintinline{haskell}{localTime} is \mintinline{haskell}{1.0e-3} seconds.+The next line corresponds to the initial state (position and velocity) of the sine generator which will be activated after the exception has been thrown,+followed by the internal counter of \mintinline{haskell}{printEverySecond}.+In the next step, local time and counter have progressed.+Two thousand steps later, the exception is finally thrown,+and the sine wave starts.++\begin{comment}+\begin{code}+newtype CountObserver = CountObserver { observe :: IO Integer }++countDebugger :: IO (Debugger IO, CountObserver)+countDebugger = do+ countRef <- newIORef 0+ observeVar <- newEmptyMVar+ let debugger = Debugger $ liveCell $ arrM $ const $ lift $ do+ n <- readIORef countRef+ putMVar observeVar n+ yield+ void $ takeMVar observeVar+ writeIORef countRef $ n + 1+ observer = CountObserver $ yield >> readMVar observeVar+ return (debugger, observer)++await :: CountObserver -> Integer -> IO ()+await CountObserver { .. } nMax = go+ where+ go = do+ n <- observe+ if n > nMax then return () else go+\end{code}+\end{comment}
+ src/LiveCoding/Debugger/StatePrint.hs view
@@ -0,0 +1,162 @@+{-# LANGUAGE GADTs #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE TypeOperators #-}++module LiveCoding.Debugger.StatePrint where++-- base+import Data.Data+import Data.Maybe (fromMaybe, fromJust)+import Data.Proxy+import Data.Typeable+import Unsafe.Coerce++-- transformers+import Control.Monad.Trans.Class (lift)+import Control.Monad.Trans.State++-- syb+import Data.Generics.Aliases+import Data.Generics.Text (gshow)++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Cell.Feedback+import LiveCoding.Debugger+import LiveCoding.Forever+import LiveCoding.Exceptions++statePrint :: Debugger IO+statePrint = Debugger $ liveCell $ arrM $ const $ do+ s <- get+ lift $ putStrLn $ stateShow s++stateShow :: Data s => s -> String+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)++compositionShow :: (Data s1, Data s2) => Composition s1 s2 -> String+compositionShow (Composition (s1, s2))+ | isUnit s1 = stateShow s2+ | isUnit s2 = stateShow s1+ | otherwise = stateShow s1 ++ " >>> " ++ stateShow s2++-- TODO Would be cooler if this was multiline+parallelShow :: (Data s1, Data s2) => Parallel s1 s2 -> String+parallelShow (Parallel (s1, s2))+ | isUnit s1 = stateShow s2+ | isUnit s2 = stateShow s1+ | 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++feedbackShow :: (Data state, Data s) => Feedback state s -> String+feedbackShow (Feedback (state, s)) = "feedback " ++ gshow s ++ " $ " ++ stateShow state++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"++choiceShow :: (Data stateL, Data stateR) => Choice stateL stateR -> String+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+liveBindShow :: (Data e, Data s1, Data s2) => LiveBindState e s1 s2 -> String+liveBindShow (NotYetThrown s1 s2) = "[NotYet " ++ stateShow s1 ++ "; " ++ stateShow s2 ++ "]"+liveBindShow (Thrown e s2) = "[Thrown " ++ gshow e ++ ". " ++ stateShow s2 ++ "]"+-}++{-+gcast2 :: forall c t t' a b. (Typeable t, Typeable t')+ => 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 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+-- sclv said to just give all the things in the where clause explicit types.+-- I guess one also needs to extend typeOf3' to include all the arguments. (Same for x/typeOf3)+-- Another possibility might be kind-heterogeneous type equality+{-+dataCast3+ :: (Typeable t, Data a)+ => (forall b c d. (Data b, Data c, Data d) => f (t b c d))+ -> Maybe (f a)+dataCast3 x = let proxy = Proxy in dropMaybe proxy $ if typeRep x == typeRep proxy+ then Just $ unsafeCoerce x+ else Nothing+dropMaybe :: Proxy a -> Maybe (f a) -> Maybe (f a)+dropMaybe _ = id+-}++--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)+ => (forall b c d. (Data b, Data c, Data d) => f (t b c d))+ -> Maybe (f a)+dataCast3 x = r + where+ r = if typeRepFingerprint (typeOf (getArg x)) == typeRepFingerprint (typeOf (getArg (fromJust r)))+ then Just $ unsafeCoerce x+ else Nothing+ getArg :: c x -> x+ getArg = undefined+-}+{-+ext3+ :: (Data a, Typeable t)+ => f a+ -> (forall b c d. (Data b, Data c, Data d) => f (t b c d))+ -> f a+--ext3 def ext = fromMaybe def $ gcast3 ext+--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 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 def ext = unQ ((Q def) `ext3` (Q ext))+++newtype Q q x = Q { unQ :: x -> q }
+ src/LiveCoding/Exceptions.lhs view
@@ -0,0 +1,154 @@+\begin{comment}+\begin{code}+{-# LANGUAGE Arrows #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE StrictData #-}++module LiveCoding.Exceptions+ ( module LiveCoding.Exceptions+ , module Control.Monad.Trans.Except+ ) where+-- TODO Don't export newtype CellExcept and Functor here and haddock mark it++-- base+import Control.Arrow+import Data.Data++-- transformers+import Control.Monad.Trans.Class+import Control.Monad.Trans.Except+import Control.Monad.Trans.Reader++-- essence-of-live-coding+import LiveCoding.Cell++\end{code}+\end{comment}++\paragraph{Throwing exceptions}+No new concepts beyond the function \mintinline{haskell}{throwE :: Monad m => e -> ExceptT e m a}+from the package \texttt{transformers} \cite{jones1995functional, transformers} are needed:+\begin{code}+throwC+ :: Monad m+ => Cell (ExceptT e m) e arbitrary+throwC = arrM throwE+\end{code}+The above function simply throws the incoming exception.+To do this only if a certain condition is satisfied,+\mintinline{haskell}{if}-constructs can be used.+For example, this cell forwards its input for a given number of seconds,+and then throws an exception:+\begin{code}+wait+ :: Monad m+ => Double+ -> Cell (ExceptT () m) a a+wait tMax = proc a -> do+ t <- localTime -< ()+ if t >= tMax+ then throwC -< ()+ else returnA -< a+\end{code}++\begin{comment}+\begin{code}+throwIf :: Monad m => (a -> Bool) -> e -> Cell (ExceptT e m) a a+throwIf condition e = proc a -> do+ if condition a+ then throwC -< e+ else returnA -< a++throwIf_ :: Monad m => (a -> Bool) -> Cell (ExceptT () m) a a+throwIf_ condition = throwIf condition ()+\end{code}+\end{comment}++\paragraph{Handling exceptions}+In usual Haskell, the \mintinline{haskell}{ExceptT} monad transformer is handled by running it:+\begin{spec}+runExceptT :: ExceptT e m b -> m (Either e b)+\end{spec}+The caller can now decide how to handle the value \mintinline{haskell}{e},+should it occur.+This approach can be adapted to cells.+A function is supplied that runs the \mintinline{haskell}{ExceptT e} layer:+\begin{code}+runExceptC+ :: (Data e, Monad m)+ => Cell (ExceptT e m) a b+ -> Cell m a (Either e b)+\end{code}+To appreciate its inner workings,+let us again look at the state it encapsulates:+\begin{code}+data ExceptState state e+ = NotThrown state+ | Exception e+ deriving Data+\end{code}+As long as no exception occurred,+\mintinline{haskell}{runExceptC cell} simply stores the state of \mintinline{haskell}{cell},+wrapped in the constructor \mintinline{haskell}{NotThrown}.+The output value \mintinline{haskell}{b} is passed on.+As soon as the exception \mintinline{haskell}{e} is thrown,+the state switches to \mintinline{haskell}{Exception e},+and the exception is output forever.+\begin{comment}+\begin{code}+runExceptC (Cell state step) = Cell { .. }+ where+ cellState = NotThrown state+ cellStep (NotThrown s) a = do+ stateExcept <- runExceptT $ step s a+ case stateExcept of+ Right (b, s')+ -> return (Right b, NotThrown s')+ Left e+ -> cellStep (Exception e) a+ cellStep (Exception e) _+ = return (Left e, Exception e)+\end{code}+\end{comment}++As soon as the exception is thrown,+we can ``live bind'' it to further cells as an extra input:+\begin{code}+(>>>=) :: (Data e1, Monad m)+ => Cell (ExceptT e1 m) a b+ -> Cell (ExceptT e2 m) (e1, a) b+ -> Cell (ExceptT e2 m) a b+(>>>=) cell1 cell2 = proc a -> do+ eb <- liftCell $ runExceptC cell1 -< a+ case eb of+ Right b -> returnA -< b+ Left e -> cell2 -< (e, a)+\end{code}+\fxwarning{If we don't do reader here, why do it with foreverE?}+We run the exception effect of the first cell.+Before it has thrown an exception, its output is simply forwarded.+As soon as the exception is thrown, the second cell is activated and fed with the input and the thrown exception.++\begin{comment}+\begin{code}+(>>>==) :: (Data e1, Monad m)+ => Cell (ExceptT e1 m) a b+ -> Cell (ReaderT e1 (ExceptT e2 m)) a b+ -> Cell (ExceptT e2 m) a b+(>>>==) cell1 cell2 = proc a -> do+ eb <- liftCell $ runExceptC cell1 -< a+ case eb of+ Left e -> runReaderC' cell2 -< (e, a)+ Right b -> returnA -< b++runReaderC' :: Cell (ReaderT r m) a b -> Cell m (r, a) b+runReaderC' Cell { .. } = Cell+ { cellStep = \state (r, a) -> runReaderT (cellStep state a) r+ , ..+ }+\end{code}+\end{comment}++\input{../essence-of-live-coding/src/LiveCoding/Preliminary/CellExcept/Newtype.lhs}+
+ src/LiveCoding/Exceptions/Finite.lhs view
@@ -0,0 +1,84 @@+\begin{comment}+\begin{code}+{-# LANGUAGE Arrows #-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}+{-# LANGUAGE TypeOperators #-}++module LiveCoding.Exceptions.Finite where++-- base+import Control.Arrow+import GHC.Generics+import Data.Data+import Data.Void++-- transformers+import Control.Monad.Trans.Except+import Control.Monad.Trans.Reader++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Exceptions (runReaderC')+-- import LiveCoding.CellExcept+\end{code}+\end{comment}++\begin{code}+class Finite e where+ commute :: Monad m => (e -> Cell m a b) -> Cell (ReaderT e m) a b++ default commute :: (Generic e, GFinite (Rep e), Monad m) => (e -> Cell m a b) -> Cell (ReaderT e m) a b+ commute handler = hoistCell (withReaderT from) $ gcommute $ handler . to++class GFinite f where+ gcommute :: Monad m => (f e -> Cell m a b) -> Cell (ReaderT (f e) m) a b++instance GFinite f => GFinite (M1 a b f) where+ gcommute handler = hoistCell (withReaderT unM1) $ gcommute $ handler . M1++instance Finite e => GFinite (K1 a e) where+ gcommute handler = hoistCell (withReaderT unK1) $ commute $ handler . K1++instance GFinite V1 where+ gcommute _ = error "gcommute: Can't commute with an empty type"++instance Finite Void where+ commute _ = error "Nope"++instance GFinite U1 where+ gcommute handler = liftCell $ handler U1++instance Finite () where++instance Finite Bool where+ commute handler = proc a -> do+ bool <- constM ask -< ()+ if bool+ then liftCell $ handler True -< a+ else liftCell $ handler False -< a++instance (GFinite eL, GFinite eR) => GFinite (eL :+: eR) where+ gcommute handler+ = let+ cellLeft = runReaderC' $ gcommute $ handler . L1+ cellRight = runReaderC' $ gcommute $ handler . R1+ gdistribute (L1 eR) a = Left (eR, a)+ gdistribute (R1 eL) a = Right (eL, a)+ in+ proc a -> do+ either12 <- constM ask -< ()+ liftCell (cellLeft ||| cellRight) -< gdistribute either12 a++instance (Finite e1, Finite e2) => Finite (Either e1 e2) where++instance (GFinite e1, GFinite e2) => GFinite (e1 :*: e2) where+ gcommute handler = hoistCell guncurryReader $ gcommute $ gcommute . gcurry handler+ where+ gcurry f e1 e2 = f (e1 :*: e2)+ guncurryReader a = ReaderT $ \(r1 :*: r2) -> runReaderT (runReaderT a r1) r2+\end{code}
+ src/LiveCoding/External.hs view
@@ -0,0 +1,54 @@+{- |+Utilities for integrating live programs into external loops, using 'IO' concurrency.+The basic idea is two wormholes (see Winograd-Court's thesis).+-}++{-# LANGUAGE Arrows #-}+{-# LANGUAGE RecordWildCards #-}+module LiveCoding.External where++-- base+import Control.Arrow+import Control.Concurrent+import Control.Monad.IO.Class++-- transformers+import Control.Monad.Trans.Reader+import Control.Monad.Trans.Writer++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Exceptions++type ExternalCell m eIn eOut a b = Cell (ReaderT eIn (WriterT eOut m)) a b++type ExternalLoop eIn eOut = Cell IO eIn eOut++runWriterC :: Monad m => Cell (WriterT w m) a b -> Cell m a (b, w)+runWriterC Cell { .. } = Cell+ { cellStep = \state a -> fmap (\((b, w), s) -> ((b, s), w)) $ runWriterT $ cellStep state a+ , ..+ }++concurrently :: MonadIO m => ExternalCell m eIn eOut a b -> IO (Cell m a b, ExternalLoop eIn eOut)+concurrently externalCell = do+ inVar <- newEmptyMVar+ outVar <- newEmptyMVar+ let+ cell = proc a -> do+ eIn <- constM (liftIO $ takeMVar inVar) -< ()+ (b, eOut) <- runWriterC (runReaderC' externalCell) -< (eIn, a)+ arrM (liftIO . putMVar outVar) -< eOut+ returnA -< b+ externalLoop = arrM (putMVar inVar) >>> constM (takeMVar outVar)+ return (cell, externalLoop)++type CellHandle a b = MVar (Cell IO a b)++makeHandle :: Cell IO a b -> IO (CellHandle a b)+makeHandle = newMVar++stepHandle :: CellHandle a b -> a -> IO b+stepHandle handle a = modifyMVar handle $ \cell -> do+ (b, cell') <- step cell a+ return (cell', b)
+ src/LiveCoding/Forever.lhs view
@@ -0,0 +1,146 @@+\begin{comment}+\begin{code}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RecordWildCards #-}++module LiveCoding.Forever where+-- base+import Control.Arrow+import Control.Concurrent (threadDelay)+import Control.Monad.Fix+import Data.Data+import Data.Void++-- transformers+import Control.Monad.Trans.Except+import Control.Monad.Trans.Reader++-- essence-of-live-coding+import LiveCoding.Bind+import LiveCoding.Cell+import LiveCoding.Exceptions+import LiveCoding.CellExcept+import LiveCoding.LiveProgram++\end{code}+\end{comment}++\subsection{Exceptions forever}++\fxwarning{Opportunity to call this an SF here (and elsewhere)}+What if we want to change between the oscillator and a waiting period indefinitely?+In other words, how do we repeatedly execute this action:+\begin{code}+sinesWaitAndTry+ :: MonadFix m+ => CellExcept m () String ()+sinesWaitAndTry = do+ try $ arr (const "Waiting...")+ >>> wait 1+ try $ sine 5+ >>> arr asciiArt+ >>> wait 5+\end{code}+\fxwarning{wait is an unintuitive name. Sounds blocking. "forwardFor"?}+The one temptation we have to resist is to recurse in the \mintinline{haskell}{CellExcept} context to prove the absence of exceptions:+\begin{code}+sinesForever'+ :: MonadFix m+ => CellExcept m () String Void+sinesForever' = do+ sinesWaitAndTry+ sinesForever'+\end{code}+It typechecks, but it does \emph{not} execute correctly.+\fxerror{Why does it hang? Does it really hang?}+As the initial state is built up,+the definition of \mintinline{haskell}{sinesForever'} inquires about the initial state of all cells in the \mintinline{haskell}{do}-expression,+but last one is again \mintinline{haskell}{foo},+and thus already initialising such a cell hangs in an infinite loop.+Using the standard function \mintinline{haskell}{forever :: Applicative f => f a -> f ()} has the same deficiency,+\fxerror{Have we tested that?}+as it is defined in essentially the same way.++The resolution is an explicit loop operator,+and faith in the library user to remember to employ it.+\begin{code}+foreverE+ :: (Monad m, Data e)+ => e+ -> Cell (ReaderT e (ExceptT e m)) a b+ -> Cell m a b+\end{code}+The loop function receives as arguments an initial exception,+and a cell that is to be executed forever\footnote{%+Of course, the monad \mintinline{haskell}{m} may again contain exceptions that can be used to break from this loop.+}.+\begin{comment}+\begin{code}+foreverE e (Cell state step) = Cell { .. }+ where+ cellState = ForeverE+ { lastException = e+ , initState = state+ , currentState = state+ }+ cellStep f@ForeverE { .. } a = do+ continueExcept <- runExceptT $ runReaderT (step currentState a) lastException+ case continueExcept of+ Left e' -> cellStep f { lastException = e', currentState = initState } a+ Right (b, state') -> return (b, f { currentState = state' })+\end{code}+\end{comment}+Again, it is instructive to look at the internal state of the looped cell:+\begin{code}+data ForeverE e s = ForeverE+ { lastException :: e+ , initState :: s+ , currentState :: s+ }+ deriving Data+\end{code}+\mintinline{haskell}{foreverE e cell} starts with the initial state of \mintinline{haskell}{cell},+and a given value \mintinline{haskell}{e}.+Then \mintinline{haskell}{cell} is stepped,+mutating \mintinline{haskell}{currentState},+until it encounters an exception.+This new exception is stored,+and the cell is restarted with the original initial state.+The cell may use the additional input \mintinline{haskell}{e}+to ask for the last thrown exception+(or the initial value, if none was thrown yet).+The exception is thus the only method of passing on data to the next loop iteration.\footnote{%+It is the user's responsibility to ensure that it does not introduce a space leak,+for example through a lazy calculation that builds up bigger and bigger thunks.+}+In our example, we need not pass on any data,+so a simpler version of the loop operator is defined:+\begin{code}+foreverC+ :: (Data e, Monad m)+ => Cell (ExceptT e m) a b+ -> Cell m a b+foreverC = foreverE () . liftCell+ . hoistCell (withExceptT $ const ())+\end{code}+Now we can finally implement our cell:+\fxwarning{Not an SF. Add MonadFix to SF defintiion?}+\begin{code}+sinesForever :: MonadFix m => Cell m () String+sinesForever = foreverC+ $ runCellExcept+ $ sinesWaitAndTry+\end{code}+\begin{code}+printSinesForever :: LiveProgram IO+printSinesForever = liveCell+ $ sinesForever+ >>> printEverySecond+\end{code}+Let us run it:+\verbatiminput{../demos/DemoSinesForever.txt}+\fxwarning{Is the [...] good or not? (Here and elsewhere)}+\fxerror{What's the advantage of forever? How to livecode with it?}++\fxerror{``Forever and ever?'' Show graceful shutdown with ExceptT. Have to change the runtime slightly for this.}+\fxnote{Awesome idea: Electrical circuits simulation where we can change the circuits live!}
+ src/LiveCoding/LiveProgram.lhs view
@@ -0,0 +1,326 @@+\begin{comment}+\begin{code}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}+module LiveCoding.LiveProgram where++-- base+import Control.Concurrent (forkIO)+import Control.Concurrent.MVar+import Control.Monad (forever)+import Data.Data++\end{code}+\end{comment}++\section{Change the program. Keep the state (as far as possible).}+\label{sec:core}+\fxerror{Split up even more into the individual modules and integrate more with the code}++Our model of a live program will consist of a state and an effectful state transition function.+A preliminary version is shown in Figure \ref{fig:LiveProgramPreliminary}.+\input{../essence-of-live-coding/src/LiveCoding/Preliminary/LiveProgram/LiveProgramPreliminary.lhs}+The program is initialised at a certain state,+and from there its behaviour is defined by repeatedly applying the function \mintinline{haskell}{liveStep} to advance the state and produce effects.+This is implemented in \mintinline{haskell}{stepProgram}.+%The type of the state should be encapsulated and thus invisible to the outside,+%it is through its effects that the live program communicates.+Since we want to run the program in a separate thread while compiling a new version of the program in the foreground,+we have to store the program in a a concurrent variable, here an \mintinline{haskell}{MVar}.+Given this variable, stepping the program it contains is a simple \mintinline{haskell}{IO} action,+implemented in \mintinline{haskell}{stepProgramMVar}.+To run the program,+we fork a background thread and repeatedly call \mintinline{haskell}{stepProgramMVar} there.++In a dynamically typed language,+such a setup is in principle enough to implement hot code swap.+At some point, the execution will be paused,+and the function \mintinline{haskell}{liveStep} is simply exchanged for a new one.+Then the execution is resumed with the new transition function,+which operates on the old state.+Of course, the new step function has to take care of migrating the state to a new format,+should this be necessary.+The difficulties arise+(apart from the practicalities of the implementation),+from the inherent unsafety of this operation:+Even if the old transition function behaved correctly,+and the old state is in a valid format,+the new transition function may crash or otherwise misbehave on the old state.+It is very hard to reduce the probability of such a failure with tests since the current state constantly changes+(by design).+A static typechecker is missing,+to guarantee the safety of this operation.++But let us return to Haskell,+where we have such a typechecker.+It immediately points out the unsafety of the migration:+There is no guarantee that the new transition function will typecheck with the old state!+In fact, in many situations, the state type needs to be extended or modified.++This kind of problem is not unknown.+In the world of databases,+it is commonplace that a table lives much longer than its initial schema.+The services accessing the data can change,+and thus also the requirements to the data format.+The solution to this problem is a \emph{schema migration},+an update to the database schema that alters the table in such a way that as little data as possible is lost,+and that the table adheres to the new schema afterwards.+Data loss is not entirely preventable, though.+If a column has to be deleted, its data will not be recoverable.+In turn, if a column is created, one has to supply a sensible default value (often \verb|NULL| will suffice).++\subsection{Migrating the state}++We can straightforwardly adopt this solution by thinking of the program state as a small database table with a single row.+Its schema is the type \mintinline{haskell}{s}.+Given a \emph{type migration} function,+we can perform hot code swap,+as shown in Figure \ref{fig:hot code swap}.+\input{../essence-of-live-coding/src/LiveCoding/Preliminary/LiveProgram/HotCodeSwap.lhs}+This may be an acceptable solution to perform a planned, well-prepared intervention,+but it does spoil the fun in a musical live coding performance if the programmer has to write a migration function after every single edit.+What a live performer actually needs,+is a function with this mysterious type signature:+\begin{spec}+hotCodeSwap+ :: LiveProgram m s'+ -> LiveProgram m s+ -> LiveProgram m s'+\end{spec}+It is the same type signature as in Figure \ref{fig:hot code swap},+but with the first argument, the manual migration function, removed.+The new program,+including its initial state,+has just been compiled,+and the old program is still stored in a concurrent variable.+Can we possibly derive the new state by simply looking at the initial state of the new program and the old state?+Is there a magical, universal migration function?+If there were, it would have this type:+\begin{spec}+migrate :: s' -> s -> s'+\end{spec}+A theoretician will probably invoke a free theorem \cite{wadler1989theorems} here,+and infer that there is in fact a unique such function:+\mintinline{haskell}{const}!+But it is not what we were hoping for.+\mintinline{haskell}{const s' s} will throw away the old state and run the program with the new initial state+-- effectively restarting our program from a blank slate.++In this generality, we cannot hope for any other solution.+But in the following, we are going to see how to tweak the live program definition by only twenty characters,+and arrive at an effective migration function.++\subsection{Type-driven migrations}+In many cases, knowing the old state and the new initial state is sufficient to derive the new, migrated state safely.+As an example, imagine the internal state of a simple webserver that counts the number of visitors to a page.+\fxwarning{Later show how migrate behaves on these examples}+\fxwarning{Lib: All examples should be in a separate directory, not in src. We should only have the final library in src.}+\fxwarning{Typecheck the example somehow? Put it in different files and make figures?}+\fxerror{Extend the example to start from Int and migrate into the newtype?}+\begin{spec}+data State = State { nVisitors :: Int }+\end{spec}+The server is initialised at 0,+and increments the number of visitors every step.+(For a full-fledged webserver,+the reader is asked to patiently wait until the next section.)+\begin{spec}+server = LiveProgram (State 0) $ \State { .. }+ -> State $ return $ nVisitors + 1+\end{spec}+\fxwarning{Show the different definitions of State as different modules by directly quoting different files as figures?+Would like to use same files like the wai demo (but hard because of Data).+Maybe rename them in order not to say Wai before we've introduced it.}+We extend the state by the name of the last user agent to access the server (initially not present):+\fxwarning{What if we add another constructor Bar here?+Could it still find out that there is a State constructor in the type?}+\begin{spec}+data State = State Int (Maybe ByteString)+initState = State 0 Nothing+\end{spec}+From just comparing the two datatype definitions,+it is apparent that we would want to keep the number of visitors,+of type \mintinline{haskell}{Int},+when migrating.+For the new argument of type \mintinline{haskell}{Maybe ByteString},+we cannot infer any sensible value from the old state,+but we can take the value \mintinline{haskell}{Nothing} from the new initial state,+and interpret it as a default value.+A general state migration function should specialise to:+\begin{spec}+migrate (Server1.State nVisitors)+ (Server2.State _ mUserAgent)+ = Server2.State nVisitors mUserAgent+\end{spec}+Our task was less obvious if we would have extended the state by the last access time,+encoded as a UNIX timestamp:+\begin{spec}+data State = State Int Int+\end{spec}+Here it is unclear to which of the \mintinline{haskell}{Int}s the old value should be migrated.+It is obvious again if the datatype was defined as a record as well:+\begin{spec}+data State = State+ { nVisitors :: Int+ , lastAccessUNIX :: Int+ }+\end{spec}+We need to copy the \mintinline{haskell}{nVisitors} field from the old state,+and initialise the \mintinline{haskell}{lastAccessUNIX} field from the new state.+(Conversely, if we were to migrate back to the original definition,+there is no way but to lose the data stored in \mintinline{haskell}{lastAccessUNIX}.)+Clearly, the record labels enabled us to identify the correct target field.+%The solution lies in the type,+%or rather, the datatype definition.+The solution lies in the datatype definition.++We can meta-program a migration function by reasoning about the structure of the type definition.+This is possible with the techniques presented in the seminal, now classic article ``Scrap Your Boilerplate'' \cite{syb}.+It supplies a typeclass \mintinline{haskell}{Typeable} which enables us to compare types and safely type-cast at runtime,+and a typeclass \mintinline{haskell}{Data} which allows us+%amongst many other features,+to inspect constructor names and record field labels.+Using the package \texttt{syb},+which supplies common utilities when working with \mintinline{haskell}{Data},+our migration function is implemented in under 50 lines of code,+with the following signature:+\begin{spec}+migrate :: (Data a, Data b) => a -> b -> a+\end{spec}+It handles the two previously mentioned cases:+Constructors with the same names,+but some mismatching arguments,+and records with some coinciding field labels,+but possibly a different order.+\fxerror{We can also do newtype wrappings}+In nested datatype definitions,+the function recurses into all children of the data tree.+Needless to say, if the types do match, then the old state is identically copied.+\fxwarning{Show examples here?}++Sometimes it is necessary to manually migrate some part of the state.+Assume, for the sake of the example,+that our webserver has become wildly popular,+and \mintinline{haskell}{nVisitors} is close to \mintinline{haskell}{maxInt}.+We need to migrate this value to an arbitrary precision \mintinline{haskell}{Integer}.+It is easy to extend \mintinline{haskell}{migrate} by a special case provided by the user:+\begin{spec}+userMigrate+ :: (Data a, Data b, Typeable c, Typeable d)+ => (c -> d)+ -> a -> b -> a++intToInteger :: Int -> Integer+intToInteger = toInteger+\end{spec}+In our example, we would use \mintinline{haskell}{userMigrate intToInteger} to migrate the state.+\fxwarning{Show example. Extend runtime.}++To use the automatic migration function,+we only need to update the live program definition to include the \mintinline{haskell}{Data} constraint,+as shown in Figure \ref{fig:LiveProgram}.+\fxwarning{Idea for elsewhere: LiveProgram m = forall s (m s, s -> m s), to ease initialisation}+\begin{figure}+\begin{code}+data LiveProgram m = forall s . Data s+ => LiveProgram+ { liveState :: s+ , liveStep :: s -> m s+ }+\end{code}++\input{../essence-of-live-coding/src/LiveCoding/LiveProgram/HotCodeSwap.lhs}++\caption{\texttt{LiveProgram.lhs}}+\label{fig:LiveProgram}+\end{figure}+This is a small restriction.+The \mintinline{haskell}{Data} typeclass can be automatically derived for every algebraic data type,+except those that incorporate \emph{functions}.+We have to refactor our live program such that all functions are contained in \mintinline{haskell}{liveStep}+(and can consequently not be migrated),+and all data is contained in \mintinline{haskell}{liveState}.++Now that we have a universal migration function,+it is not necessary to carry the type of the state around in the type signature.+In fact it would be cumbersome in combination with \mintinline{haskell}{MVar}s+(which can't change their type),+and a real burden when later modularising the state.+Consequently, the type is made existential.+The only necessary information is that it is an instance of \mintinline{haskell}{Data}.++\begin{comment}+\begin{code}+-- | 'mappend' here is _not_ the migration function! (Compare 'migrate'.)+-- This instance simply tuples both states and performs the steps sequentially.+instance Monad m => Semigroup (LiveProgram m) where+ LiveProgram state1 step1 <> LiveProgram state2 step2 = LiveProgram { .. }+ where+ liveState = (state1, state2)+ liveStep (state1, state2) = do+ state1' <- step1 state1+ state2' <- step2 state2+ return (state1', state2')++instance Monad m => Monoid (LiveProgram m) where+ mempty = LiveProgram () return+\end{code}+\end{comment}++\input{../essence-of-live-coding/src/LiveCoding/RuntimeIO.lhs}++\subsection{Live coding a webserver}++\fxwarning{Consider redoing this as a GHCi session where we call the server from within Haskell, e.g. with the curl or a HTTP package}++To show that live coding can be applied to domains outside audio and video applications,+let us realise the example from the previous section and create a tiny webserver using the WAI/Warp framework \cite{Warp}.+It is supposed to count the number of visitors,+and keep this state in memory when we change the implementation.++The boiler plate code, which is suppressed here,+initialises the Warp server,+uses \mintinline{haskell}{launch} to start our live program in a separate thread+and waits for user input to update it.++To save ourselves an introduction to Warp,+we will communicate to it via two \mintinline{haskell}{MVar}s,+which we need to share with the live program.+The textbook solution is to supply the variables through a \mintinline{haskell}{Reader} environment,+\begin{comment}+We have to generalise the definition of live programs once more,+to arbitrary monads.+The final version is given in Figure \ref{fig:LiveProgram}.+\end{comment}+which needs to supplied to the live program before execution.+This can be done by transporting the program along the \mintinline{haskell}{runReaderT} monad morphism.+A function \mintinline{haskell}{hoistLiveProgram} does this+(borrowing nomenclature from the \texttt{mmorph} \cite{mmorph} package).+\begin{comment}+Abstracting this operation, we need a utility that applies a monad morphism to a live program.+(Borrowing nomenclature from the \texttt{mmorph} package,+we call it \mintinline{haskell}{hoistLiveProgram}.)+\fxwarning{Try to merge with the previous figure. (My hoist explanation is also very clunky)}+\begin{figure}+\begin{spec}+data LiveProgram m = forall s . Data s+ => LiveProgram+ { liveState :: s+ , liveStep :: s -> m s+ }+\end{spec}+\begin{code}+hoistLiveProgram+ :: (forall a . m1 a -> m2 a)+ -> LiveProgram m1+ -> LiveProgram m2+hoistLiveProgram morph LiveProgram { .. } = LiveProgram+ { liveStep = morph . liveStep+ , ..+ }+\end{code}+\caption{LiveProgram.lhs}+\label{fig:LiveProgram}+\end{figure}+\end{comment}
+ src/LiveCoding/LiveProgram/HotCodeSwap.lhs view
@@ -0,0 +1,26 @@+\begin{comment}+\begin{code}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE ExistentialQuantification #-}++module LiveCoding.LiveProgram.HotCodeSwap where++-- essence-of-live-coding+import LiveCoding.LiveProgram+import LiveCoding.Migrate++\end{code}+\end{comment}+\begin{code}+hotCodeSwap+ :: LiveProgram m+ -> LiveProgram m+ -> LiveProgram m+hotCodeSwap+ (LiveProgram newState newStep)+ (LiveProgram oldState _)+ = LiveProgram+ { liveState = migrate newState oldState+ , liveStep = newStep+ }+\end{code}
+ src/LiveCoding/Migrate.lhs view
@@ -0,0 +1,91 @@+\begin{comment}+\begin{code}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE RankNTypes #-}+module LiveCoding.Migrate where++-- base+import Data.Data+import Data.Functor ((<&>))+import Data.Maybe+import Prelude hiding (GT)++-- syb+import Data.Generics.Aliases+import Data.Generics.Twins++-- essence-of-live-coding+import LiveCoding.Migrate.Debugger+import LiveCoding.Migrate.Migration+\end{code}+\end{comment}++\begin{code}+-- | The standard migration solution, recursing into the data structure and applying 'standardMigration'.+migrate :: (Data a, Data b) => a -> b -> a+migrate = migrateWith standardMigration++-- | Still recurse into the data structure, but apply your own given migration.+-- Often you will want to call @migrateWith (standardMigration <> yourMigration)@.+migrateWith :: (Data a, Data b) => Migration -> a -> b -> a+migrateWith specific = runSafeMigration $ treeMigration specific++-- | Covers standard cases such as matching types, to and from debuggers, to newtypes.+standardMigration :: Migration+standardMigration = castMigration <> migrationDebugging <> newtypeMigration++-- | Wrapping 'treeMigrateWith' in the newtype.+treeMigration :: Migration -> Migration+treeMigration migration = Migration $ treeMigrateWith migration++-- | The standard migration working horse.+-- Tries to apply the given migration,+-- and if this fails, tries to recurse into the data structure.+treeMigrateWith+ :: (Data a, Data b)+ => Migration+ -> a -> b -> Maybe a++-- Maybe the specified user migration works?+treeMigrateWith specific a b+ | Just a' <- runMigration specific a b+ = Just a'++-- Maybe it's an algebraic datatype.+-- Let's try and match the structure as well as possible.+treeMigrateWith specific a b+ | isAlgType typeA && isAlgType typeB+ && show typeA == show typeB+ && showConstr constrA == showConstr constrB+ = Just migrateSameConstr+ where+ typeA = dataTypeOf a+ typeB = dataTypeOf b+ constrA = toConstr a+ constrB = toConstr b+ constrFieldsA = constrFields constrA+ constrFieldsB = constrFields constrB+ migrateSameConstr+ -- We have records, we can match on the field labels+ | (not $ null constrFieldsA)+ && (not $ null constrFieldsB)+ = setChildren getFieldSetters a+ -- One of the two is not a record, just try to match 1-1 as far as possible+ | otherwise = setChildren (getChildrenSetters specific b) a+ settersB = zip constrFieldsB $ getChildrenSetters specific b+ getFieldSetters = constrFieldsA <&>+ \field -> fromMaybe (GT id)+ $ lookup field settersB++-- Defeat. No migration worked.+treeMigrateWith _ _ _ = Nothing++getChildrenSetters :: Data a => Migration -> a -> [GenericT']+getChildrenSetters specific = gmapQ $ \child -> GT $ flip (runSafeMigration $ treeMigration specific) child++setChildren :: Data a => [GenericT'] -> a -> a+setChildren updates a = snd $ gmapAccumT f updates a+ where+ f [] e = ([], e)+ f (update : updates) e = (updates, unGT update $ e)+\end{code}
+ src/LiveCoding/Migrate/Debugger.hs view
@@ -0,0 +1,41 @@+{-# LANGUAGE NamedFieldPuns #-}+{-# LANGUAGE RecordWildCards #-}+module LiveCoding.Migrate.Debugger where++-- base+import Control.Monad (guard)+import Data.Data+import Data.Maybe++-- essence-of-live-coding+import LiveCoding.Debugger+import LiveCoding.Migrate.Migration++migrateToDebugging+ :: Debugging dbgState state+ -> state+ -> Debugging dbgState state+migrateToDebugging Debugging { dbgState } state = Debugging { .. }++-- | Tries to cast the current state into the joint state of debugger and program.+-- Will cast to the program state if possible, or else try to cast to the debugger state.+migrationToDebugging :: Migration+migrationToDebugging = Migration $ \a b -> do+ guard $ ("Debugging" ==) $ dataTypeName $ dataTypeOf a+ gmapMo (const $ cast b) a++-- | Try to extract a state from the current joint state of debugger and program.+migrationFromDebugging :: Migration+migrationFromDebugging = Migration $ \_ b -> do+ guard $ ("Debugging" ==) $ dataTypeName $ dataTypeOf b+ listToMaybe $ catMaybes $ (gmapQ cast) b++migrateFromDebugging+ :: state+ -> Debugging dbgState state+ -> state+migrateFromDebugging _state Debugging { state } = state++-- | Combines 'migrationToDebugging' and 'migrationFromDebugging'.+migrationDebugging :: Migration+migrationDebugging = migrationToDebugging <> migrationFromDebugging
+ src/LiveCoding/Migrate/Migration.hs view
@@ -0,0 +1,52 @@+{-# LANGUAGE RankNTypes #-}++module LiveCoding.Migrate.Migration where++-- base+import Control.Monad (guard)+import Data.Data+import Data.Maybe (fromMaybe)+import Data.Monoid++-- syb+import Data.Generics.Schemes (glength)++data Migration = Migration+ { 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 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)++instance Monoid Migration where+ mempty = Migration $ const $ const Nothing++castMigration :: Migration+castMigration = Migration $ const cast++newtypeMigration :: Migration+newtypeMigration = Migration $ \a b -> do+ -- Is it an algebraic datatype with a single constructor?+ AlgRep [_constr] <- return $ dataTypeRep $ dataTypeOf a+ -- Does the constructor have a single argument?+ guard $ glength a == 1+ -- 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+userMigration specific = Migration $ \_a b -> cast =<< specific <$> cast b
+ src/LiveCoding/Preliminary/CellExcept.lhs view
@@ -0,0 +1,77 @@+\begin{comment}+\begin{code}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE RecordWildCards #-}++module LiveCoding.Preliminary.CellExcept where++-- base+import Control.Arrow+import Data.Data+import Data.Either (fromRight)+import Data.Void++-- transformers+import Control.Monad.Trans.Reader+import Control.Monad.Trans.Except++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Preliminary.CellExcept.Applicative+import LiveCoding.Exceptions+\end{code}+\end{comment}++\paragraph{Using exceptions}+\fxwarning{We didn't mention the newtype in the last paragraph, this is maybe confusing}+We can enter the \mintinline{haskell}{CellExcept} context from an exception-throwing cell,+trying to execute it until the exception occurs:+\fxerror{This doesn't work here anymore because we haven't explained how it's a newtype.+Also we already know that try needs an extra type class. Take this from the monad section.}+\begin{code}+try+ :: Data e+ => Cell (ExceptT e m) a b+ -> CellExcept m a b e+try = CellExcept id+\end{code}+And we can leave it safely once we have proven that there are no exceptions left to throw,+i.e. the exception type is empty:+\fxerror{I'm using runCellExcept which wasn't explained yet}+\begin{code}+safely+ :: Monad m+ => CellExcept m a b Void+ -> Cell m a b+safely = hoistCell discardVoid . runCellExcept++discardVoid+ :: Functor m+ => ExceptT Void m a+ -> m a+discardVoid+ = fmap (either absurd id) . runExceptT+\end{code}+One way to prove the absence of further exceptions is,+of course, to run an exception-free cell:+\begin{code}+safe :: Monad m => Cell m a b -> CellExcept m a b void+safe cell = CellExcept+ { fmapExcept = absurd+ , cellExcept = liftCell cell+ }+\end{code}+If we want to leave an exception unhandled,+this is also possible:+\begin{code}+runCellExcept+ :: Monad m+ => CellExcept m a b e+ -> Cell (ExceptT e m) a b+runCellExcept CellExcept { .. }+ = hoistCell (withExceptT fmapExcept)+ cellExcept+\end{code}+This is especially useful for shutting down a live program gracefully,+using \mintinline{haskell}{e} as the exit code.+\fxerror{But we haven't implemented that yet. And also can only do that with a more general "reactimate"}
+ src/LiveCoding/Preliminary/CellExcept/Applicative.lhs view
@@ -0,0 +1,102 @@+\begin{comment}+\begin{code}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE TupleSections #-}++module LiveCoding.Preliminary.CellExcept.Applicative where++-- base+import Data.Data++-- transformers+import Control.Monad.Trans.Except+import Control.Monad.Trans.Reader++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Exceptions++\end{code}+\end{comment}++\paragraph{Applying it to \mintinline{haskell}{Applicative}}+If we are allowed to read the first exception during the execution of the second cell,+we can simply re-raise it once the second exception is thrown:+\begin{code}+andThen+ :: (Data e1, Monad m)+ => Cell (ExceptT e1 m) a b+ -> Cell (ExceptT e2 m) a b+ -> Cell (ExceptT (e1, e2) m) a b+cell1 `andThen` Cell { .. } = cell1 >>>= Cell+ { cellStep = \state (e1, a) ->+ withExceptT (e1, ) $ cellStep state a+ , ..+ }+\end{code}++\begin{comment}+\begin{spec}+ hoistCell readException cell2+ where+ readException+ :: Functor m+ => ExceptT e2 m x+ -> ReaderT e1 (ExceptT(e1, e2) m) x+ readException exception = ReaderT+ $ \e1 -> withExceptT (e1, ) exception+\end{spec}+\end{comment}+Given two \mintinline{haskell}{Cell}s,+the first may throw an exception,+upon which the second cell gains control.+As soon as it throws a second exception,+both exceptions are thrown as a tuple.++At this point, we unfortunately have to give up the efficient \mintinline{haskell}{newtype}.+The spoilsport is, again the type class \mintinline{haskell}{Data},+to which the exception type \mintinline{haskell}{e1} is subjected+(since the exception must be stored during the execution of the second cell).+But the issue is minor,+it is fixed by defining the \emph{free functor},+or \emph{Co-Yoneda construction}:+\fxwarning{Maybe cite http://comonad.com/reader/2016/adjoint-triples/ or search something else}+\fxwarning{Possible other names: Mode}+\begin{code}+data CellExcept m a b e = forall e' .+ Data e' => CellExcept+ { fmapExcept :: e' -> e+ , cellExcept :: Cell (ExceptT e' m) a b+ }+\end{code}+While ensuring that we only store cells with exceptions that can be \emph{bound},+we do not restrict the parameter type \mintinline{haskell}{e}.++It is known that this construction gives rise to a \mintinline{haskell}{Functor} instance for free:+\begin{code}+instance Functor (CellExcept m a b) where+ fmap f CellExcept { .. } = CellExcept+ { fmapExcept = f . fmapExcept+ , ..+ }+\end{code}++The \mintinline{haskell}{Applicative} instance arises from the work we have done so far.+\mintinline{haskell}{pure} is implemented by throwing a unit and transforming it to the required exception,+while sequential application is a bookkeeping exercise around the previously defined function \mintinline{haskell}{andThen}:+\begin{code}+instance Monad m+ => Applicative (CellExcept m a b) where+ pure e = CellExcept+ { fmapExcept = const e+ , cellExcept = constM $ throwE ()+ }++ CellExcept fmap1 cell1 <*>+ CellExcept fmap2 cell2 = CellExcept { .. }+ where+ fmapExcept (e1, e2) = fmap1 e1+ $ fmap2 e2+ cellExcept = cell1 `andThen` cell2+\end{code}
+ src/LiveCoding/Preliminary/CellExcept/Monad.lhs view
@@ -0,0 +1,117 @@+\begin{comment}+\begin{code}+{-# LANGUAGE Arrows #-}++module LiveCoding.Preliminary.CellExcept.Monad where++-- transformers+import Control.Monad.Trans.Except+import Control.Monad.Trans.Reader++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Exceptions++\end{code}+\end{comment}++\subsection{Finite patience with monads}+\fxerror{Explain that we're using applicative do, and use it in example?}+While \mintinline{haskell}{Applicative} control flow is certainly appreciated,+and the live bind combinator \mintinline{haskell}{>>>=} is even more expressive,+it still encourages boilerplate code like the following:+\fxerror{Replace by actual example}+\begin{spec}+throwBool >>>= proc (bool, a) -> do+ if bool+ then foo1 -< a+ else foo2 -< a+\end{spec}+The annoyed library user will promptly abbreviate this pattern:+\fxerror{Rewrite such as to write a commute-like function, as motivation for the type class}+\begin{code}+bindBool+ :: Monad m+ => Cell (ExceptT Bool m) a b+ -> (Bool -> Cell (ExceptT e m) a b)+ -> Cell (ExceptT e m) a b+bindBool cell handler+ = cell >>>= proc (bool, a) -> do+ if bool+ then handler True -< a+ else handler False -< a+\end{code}+\fxwarning{And now use bindBool to rewrite the upper example}+\begin{comment}+\begin{code}+{-+bindBool'+ :: (Monad m, Data e, Finite e)+ => CellExcept m a b Bool+ -> (Bool -> CellExcept m a b e)+ -> CellExcept m a b e+bindBool' cellE handler = CellExcept+ { fmapExcept = id+ , cellExcept = runCellExcept cellE `bindBool` (runCellExcept . handler)+ }+-}+\end{code}+\fxerror{Finish the wrapped thing}+\end{comment}+\fxerror{We have a Data e here suddenly.+Can we be cleverer than id?}+But, behold!+Up to the \mintinline{haskell}{CellExcept} wrapper,+we have just implemented bind,+the holy grail which we assumed to be denied!+The bound type is restricted to \mintinline{haskell}{Bool},+admitted,+but if it is possible to bind \mintinline{haskell}{Bool},+then it is certainly possible to bind \mintinline{haskell}{(Bool, Bool)},+by nesting two \mintinline{haskell}{if}-statements.+By the same logic, we can bind \mintinline{haskell}{(Bool, Bool, Bool)} %, +%\mintinline{haskell}{(Bool, Bool, Bool, Bool)},+and so on+(and of course any isomorphic type as well).+In fact, \emph{any finite type} can be bound in principle,+by embedding it in such a binary vector.+For what follows, we will only consider finite algebraic datatypes.+These are essentially the unit type (or any single constructor type),+sum types (or multiple constructor types) of other finite types,+and product types (or multiple argument constructors).+Recursive datatypes are infinite in Haskell+(consider, e.g., the list type).++How can it be that a general bind function does not type-check,+but we can implement one for any finite type?+If the exception type \mintinline{haskell}{e} is finite,+the type checker can inspect the state type of the cell \mintinline{haskell}{handler e}+for every possible exception value,+\emph{at compile time}.+All that is needed is a little help to spell out all the possible cases,+as has been done for \mintinline{haskell}{Bool}.+\fxerror{Wow! This means that the control state of such live programs is always finite! This means e.g. that we can completely analyse CTL on it!}++But certainly, we don't want to write out all possible values of a type before we can bind it.+Again, the Haskellers' aversion to boilerplate has created a solution that can be tailored to our needs:+Generic deriving \cite{GenericDeriving}.+We simply need to implement a bind function for generic sum types and product types,+then this function can be abstracted into a type class,+and GHC can infer a default instance for every algebraic data type by adding a single line of boilerplate.+Since the type class is defined for all finite algebraic datatypes, we will call it \mintinline{haskell}{Finite}.+\fxerror{Example}+Any user-contributed or standard type can be an instance this type class,+given that it is not recursive.+\fxerror{We omitted functions! But this isn't such a big problem since they don't have a Data instance anyways.}++It is possible to restrict the previous \mintinline{haskell}{CellExcept} definition by the typeclass:+\begin{spec}+data CellExcept m a b e = forall e' .+ (Data e', Finite e') => CellExcept+ { fmapExcept :: e' -> e+ , cellExcept :: Cell (ExceptT e' m) a b+ }+\end{spec}+Implementing the individual bind functions for sums and products,+and finally writing down the complete \mintinline{haskell}{Monad} instance is a tedious exercise in Generic deriving.+\input{../essence-of-live-coding/src/LiveCoding/CellExcept.lhs}
+ src/LiveCoding/Preliminary/CellExcept/Newtype.lhs view
@@ -0,0 +1,99 @@+\begin{comment}+\begin{code}+module LiveCoding.Preliminary.CellExcept.Newtype where++-- base+import Control.Arrow+import Data.Void++-- transformers+import Control.Monad.Trans.Except++-- essence-of-live-coding+import LiveCoding.Cell+import LiveCoding.Exceptions+\end{code}+\end{comment}++\subsection{Control flow context}+\label{sec:control flow context}+%\paragraph{Wrapping exceptions}+Inspired by \cite[Section 2, "Control Flow through Exceptions"]{Rhine},+%we create our own control flow context,+%by introducing a newtype:+we introduce a newtype:++\begin{code}+newtype CellExcept m a b e = CellExcept+ { runCellExcept :: Cell (ExceptT e m) a b }+\end{code}++We can enter the \mintinline{haskell}{CellExcept} context from an exception-throwing cell,+trying to execute it until the exception occurs:+\begin{code}+try+ :: Cell (ExceptT e m) a b+ -> CellExcept m a b e+try = CellExcept+\end{code}+And we can leave it safely once we have proven that there are no exceptions left to throw,+i.e. the exception type is empty:+\begin{code}+safely+ :: Monad m+ => CellExcept m a b Void+ -> Cell m a b+safely = hoistCell discardVoid . runCellExcept+ where+ discardVoid+ = fmap (either absurd id) . runExceptT+\end{code}+One way to prove the absence of further exceptions is,+of course, to run an exception-free cell:+\begin{code}+safe+ :: Monad m+ => Cell m a b+ -> CellExcept m a b Void+safe cell = CellExcept $ liftCell cell+\end{code}++\paragraph{The return of the monad}+Our new hope is to give \mintinline{haskell}{Functor}, \mintinline{haskell}{Applicative} and \mintinline{haskell}{Monad} instances to \mintinline{haskell}{CellExcept}.+We will explore now how this allows for rich control flow.++The \mintinline{haskell}{Functor} instance is not too hard.+When an exception is raised,+we simply apply a given function to it:+\begin{code}+instance Functor m+ => Functor (CellExcept m a b) where+ fmap f (CellExcept cell) = CellExcept+ $ hoistCell (withExceptT f) cell+\end{code}++The \mintinline{haskell}{pure} function of the \mintinline{haskell}{Applicative} class+(or equivalently, \mintinline{haskell}{return} of the \mintinline{haskell}{Monad}),+is simply throwing an exception,+wrapped in the newtype:+\begin{code}+pure+ :: Monad m+ => e+ -> CellExcept m a b e+pure e = CellExcept $ arr (const e) >>> throwC+\end{code}++Like the sequential application operator \mintinline{haskell}{<*>} from the \mintinline{haskell}{Applicative} class+can be defined from the bind operator \mintinline{haskell}{>>=},+it can also be defined from the \emph{live bind} operator \mintinline{haskell}{>>>=} introduced previously.+As a technical tour-de-force,+even a \mintinline{haskell}{Monad} instance for \mintinline{haskell}{CellExcept} can be derived with some modifications.+This is shown at length in the appendix.++But how can \mintinline{haskell}{Applicative} and \mintinline{haskell}{Monad} be put to use?+The foreground value of \mintinline{haskell}{CellExcept} is the thrown exception.+With \mintinline{haskell}{pure}, such values are created,+and \mintinline{haskell}{Functor} allows us to perform computations with them.+The classes \mintinline{haskell}{Applicative} and \mintinline{haskell}{Monad} allow us to \emph{chain} the execution of exception throwing cells:+\fxwarning{Comment on how Monad is even stronger than Applicative?}
+ src/LiveCoding/Preliminary/LiveProgram/HotCodeSwap.lhs view
@@ -0,0 +1,62 @@+\begin{comment}+\begin{code}+{-# LANGUAGE RecordWildCards #-}++module LiveCoding.Preliminary.LiveProgram.HotCodeSwap where++-- base+import Control.Concurrent+import Control.Monad (forever)++-- essence-of-live-coding+import LiveCoding.Preliminary.LiveProgram.LiveProgramPreliminary+\end{code}+\end{comment}++\begin{figure}+\begin{code}+hotCodeSwap+ :: (s -> s')+ -> LiveProgram m s'+ -> LiveProgram m s+ -> LiveProgram m s'+hotCodeSwap migrate newProgram oldProgram+ = LiveProgram+ { liveState = migrate $ liveState oldProgram+ , liveStep = liveStep newProgram+ }+\end{code}+\caption{\texttt{Preliminary/HotCodeSwap.lhs}}+\label{fig:hot code swap}+\end{figure}+\fxwarning{The thing with the MVar doesn't work on the spot anymore. But it can still work with a "typed" handle. Every time you swap, you get a new handle that carries the currently saved type. Worth commenting upon?+It's somewhat complicated: We have to kill the old MVar and create a new one every time we update. Then we also have to update the ticking function}+\begin{comment}+\begin{code}+type LiveRef s = (MVar (LiveProgram IO s), MVar (IO ()))+launch :: LiveProgram IO s -> IO (LiveRef s)+launch liveProg = do+ progVar <- newMVar liveProg+ tickVar <- newMVar $ tick progVar+ forkIO $ forever $ do+ action <- takeMVar tickVar+ action+ tryPutMVar tickVar action+ return (progVar, tickVar)++tick :: MVar (LiveProgram IO s) -> IO ()+tick var = do+ LiveProgram {..} <- takeMVar var+ liveState' <- liveStep liveState+ putMVar var LiveProgram { liveState = liveState', .. }++swapWith :: (s -> s') -> LiveProgram IO s' -> LiveRef s -> IO (LiveRef s')+swapWith migrate (LiveProgram _newState newStep) (progVar, actionVar) = do+ _ <- takeMVar actionVar+ LiveProgram oldState oldStep <- takeMVar progVar+ let newProg = LiveProgram (migrate oldState) newStep+ newProgVar <- newMVar newProg+ putMVar actionVar $ tick newProgVar+ return (newProgVar, actionVar)+\end{code}+\end{comment}
+ src/LiveCoding/Preliminary/LiveProgram/LiveProgram2.lhs view
@@ -0,0 +1,22 @@+\begin{figure}+\begin{comment}+\begin{code}+{-# LANGUAGE ExistentialQuantification #-}++module LiveCoding.Preliminary.LiveProgram.LiveProgram2 where++-- base+import Data.Data+\end{code}+\end{comment}+\begin{code}+data LiveProgram = forall s . Data s+ => LiveProgram+ { liveState :: s+ , liveStep :: s -> IO s+ }+\end{code}+\fxerror{Compile these as well}+\caption{\texttt{LiveProgram2.lhs}}+\label{fig:LiveProgram2}+\end{figure}
+ src/LiveCoding/Preliminary/LiveProgram/LiveProgramPreliminary.lhs view
@@ -0,0 +1,48 @@+\begin{figure}+\begin{comment}+\begin{code}+{-# LANGUAGE RecordWildCards #-}++module LiveCoding.Preliminary.LiveProgram.LiveProgramPreliminary where++-- base+import Control.Concurrent+import Control.Monad (forever)++\end{code}+\end{comment}+\begin{code}+data LiveProgram m s = LiveProgram+ { liveState :: s+ , liveStep :: s -> m s+ }+\end{code}+\begin{code}+stepProgram+ :: Monad m+ => LiveProgram m s -> m (LiveProgram m s)+stepProgram liveProgram@LiveProgram { .. } = do+ liveState' <- liveStep liveState+ return liveProgram { liveState = liveState' }+\end{code}+\begin{code}+stepProgramMVar+ :: MVar (LiveProgram IO s)+ -> IO ()+stepProgramMVar var = do+ currentProgram <- takeMVar var+ nextProgram <- stepProgram currentProgram+ putMVar var nextProgram+\end{code}+\begin{code}+launch+ :: LiveProgram IO s+ -> IO (MVar (LiveProgram IO s))+launch liveProgram = do+ var <- newMVar liveProgram+ forkIO $ forever $ stepProgramMVar var+ return var+\end{code}+\caption{\texttt{LiveProgramPreliminary.lhs}}+\label{fig:LiveProgramPreliminary}+\end{figure}
+ src/LiveCoding/RuntimeIO.lhs view
@@ -0,0 +1,178 @@+\begin{comment}+\begin{code}+{-# LANGUAGE BangPatterns #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE RecordWildCards #-}++module LiveCoding.RuntimeIO where++-- base+import Control.Arrow+import Control.Concurrent+import Control.Monad+import Data.Data++-- essence-of-live-coding+import LiveCoding.LiveProgram+import LiveCoding.LiveProgram.HotCodeSwap+import LiveCoding.Debugger+import LiveCoding.Migrate++stepProgram :: Monad m => LiveProgram m -> m (LiveProgram m)+stepProgram LiveProgram {..} = do+ liveState' <- liveStep liveState+ return LiveProgram { liveState = liveState', .. }++stepProgramMVar+ :: MVar (LiveProgram IO)+ -> IO ()+stepProgramMVar var = do+ currentProgram <- takeMVar var+ nextProgram <- stepProgram currentProgram+ putMVar var nextProgram+\end{code}+\end{comment}++\section{The runtime}+\label{sec:runtime}++\subsection{Hands on interaction}+Enough declaration.+Let us get semantic and run some live programs!+In the preliminary version,+a function \mintinline{haskell}{stepProgram} implemented a single execution step,+and it can be reused here,+up to removing the explicit state type.+The runtime behaviour of a live program is defined by calling this function repeatedly.+We could of course run the program in the foreground thread:+\begin{code}+foreground :: Monad m => LiveProgram m -> m ()+foreground liveProgram+ = stepProgram liveProgram+ >>= foreground+\end{code}+But this would leave no possibility to exchange the program with a new one.+%But this would then become the main loop,+%and leave no control to exchange the program with a new one.+Instead, we can store the program in an \mintinline{haskell}{MVar}+and call \mintinline{haskell}{stepProgramMVar} on it.+Now that we can migrate any \mintinline{haskell}{Data},+we can follow the original plan of exchanging the live program in mid-execution:+\begin{code}+update+ :: MVar (LiveProgram IO)+ -> LiveProgram IO+ -> IO ()+update var newProg = do+ oldProg <- takeMVar var+ putMVar var $ hotCodeSwap newProg oldProg+\end{code}+The old program is retrieved from the concurrent variable,+migrated to the new state,+and put back for further execution.+And so begins our first live coding session in GHCi+(line breaks added for readability):+\begin{verbatim}+ > var <- newMVar $ LiveProgram 0+ $ \s -> print s >> return (s + 1)+ > stepProgramMVar var+0+ > stepProgramMVar var+1+ > update var $ LiveProgram 0+ $ \s -> print s >> return (s - 1)+ > stepProgramMVar var+2+ > stepProgramMVar var+1+ > stepProgramMVar var+0+\end{verbatim}+%When the live program was updated,+Upon updating,+the state was correctly preserved.+The programs were specified in the interactive session here,+but of course we will want to load the program from a file,+and use GHCi's \texttt{:reload} functionality when we have edited it.+But as soon as we do this,+the local binding \mintinline{haskell}{var} is lost.+The package \texttt{foreign-store} \cite{foreign-store} offers a remedy:+\mintinline{haskell}{var} can be stored persistently across reloads.+To facilitate its usage, GHCi macros are defined for the initialisation and reload operations (Figure \ref{fig:ghci}).+\input{../essence-of-live-coding-ghci/src/LiveCoding/GHCi.lhs}+They assume the main live program and the \mintinline{haskell}{MVar} to be called \mintinline{haskell}{liveProgram} and \mintinline{haskell}{var},+respectively,+but this can of course be generalised.+With the macros loaded, the session simplifies to:+\begin{verbatim}+ > :liveinit+ > :livestep+0+ > :livestep+1+ > :livereload+[1 of 1] Compiling Main ( ... )+Ok, one module loaded.+ > :livestep+2+ > :livestep+1+ > :livestep+0+\end{verbatim}+Before entering \texttt{:livereload},+the main file was edited in place and reloaded.+\begin{comment}+\begin{code}+launch :: LiveProgram IO -> IO (MVar (LiveProgram IO))+launch liveProg = do+ var <- newMVar liveProg+ forkIO $ background var+ return var++launchWithDebugger :: LiveProgram IO -> Debugger IO -> IO (MVar (LiveProgram IO))+launchWithDebugger liveProg debugger = launch $ liveProg `withDebugger` debugger+{-+ var <- newMVar liveProg+ forkIO $ backgroundWithDebugger var debugger+ return var+-}++{-+debug :: Debugger_ -> LiveProgram IO -> IO (LiveProgram IO)+debug Debugger_ { .. } LiveProgram { .. } = do+ liveState' <- debugState liveState+ return LiveProgram { liveState = liveState', .. }++backgroundWithDebugger :: MVar (LiveProgram IO) -> Debugger_ -> IO ()+backgroundWithDebugger var debugger = forever $ do+ liveProg <- takeMVar var+ liveProg' <- stepProgram liveProg+ liveProg'' <- debug debugger liveProg'+ putMVar var liveProg''+-}++background :: MVar (LiveProgram IO) -> IO ()+background var = forever $ do+ liveProg <- takeMVar var+ liveProg' <- stepProgram liveProg+ putMVar var liveProg'++{-+-- Old version where combine was called from background+combine :: MVar (LiveProgram IO) -> LiveProgram IO -> IO ()+combine var prog = do+ success <- tryPutMVar var prog+ unless success $ do+ newProg <- takeMVar var+ combine var $ hotCodeSwap prog newProg+-}+\end{code}+\end{comment}+Of course,+it is not intended to enter \texttt{:livestep} repeatedly when coding.+We want to launch a separate thread which executes the steps in the background.+Again, we can reuse the function \mintinline{haskell}{launch}.+(Only the type signature needs updating.)+In the next subsection,+a full example is shown.
+ test/Main.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE ScopedTypeVariables #-}++-- test-framework+import Test.Framework++-- test-framework-quickcheck2+import Test.Framework.Providers.QuickCheck2++-- QuickCheck+import Test.QuickCheck++-- essence-of-live-coding+import LiveCoding+import qualified TestData.Foo1 as Foo1+import qualified TestData.Foo2 as Foo2++intToInteger :: Int -> Integer+intToInteger = toInteger++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 "Records"+ [ testProperty "" $ Foo1.foo' === migrate Foo1.foo Foo2.foo+ , testProperty "" $ Foo2.foo' === migrate Foo2.foo Foo1.foo+ , testProperty "" $ Foo2.bar' === migrate Foo2.bar Foo1.bar+ , testProperty "" $ Foo2.baz' === migrate Foo2.baz Foo1.baz+ ]+ , testGroup "User migration"+ [ testProperty "" $ Foo2.frob' === migrateWith (userMigration intToInteger) Foo2.frob Foo1.frob+ ]+ , testGroup "Newtype wrapping"+ [ testProperty "" $ \(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 }+ ]+ ]
+ test/TestData/Foo1.hs view
@@ -0,0 +1,38 @@+{-# LANGUAGE DeriveDataTypeable #-}+module TestData.Foo1 where++-- base+import Data.Data+import Data.Typeable++data Foo = Foo Integer Bool+ deriving (Show, Eq, Typeable, Data)++foo = Foo 1 False+foo' = Foo 2 False++data Bar = Bar+ { barA :: Integer+ , barD :: Integer+ , barC :: Bool+ }+ deriving (Show, Eq, Typeable, Data)++bar = Bar+ { barA = 23+ , barD = 5+ , barC = True+ }++data Baz = Baz+ { bazFoo :: Foo+ , bazBar :: Bar+ }+ deriving (Show, Eq, Typeable, Data)++baz = Baz foo bar++data Frob = Frob Int+ deriving (Show, Eq, Typeable, Data)++frob = Frob 1
+ test/TestData/Foo2.hs view
@@ -0,0 +1,46 @@+{-# LANGUAGE DeriveDataTypeable #-}+module TestData.Foo2 where++-- base+import Data.Data+import Data.Typeable++data Foo = Foo Integer+ deriving (Show, Eq, Typeable, Data)++foo = Foo 2+foo' = Foo 1++data Bar = Bar+ { barB :: Integer+ , barA :: Integer+ , barC :: String+ }+ deriving (Show, Eq, Typeable, Data)++bar = Bar+ { barB = 42+ , barA = 100+ , barC = "Bar"+ }++bar' = Bar+ { barB = 42+ , barA = 23+ , barC = "Bar"+ }++data Baz = Baz+ { bazBar :: Bar+ , bazFoo :: Foo+ }+ deriving (Show, Eq, Typeable, Data)++baz = Baz bar foo+baz' = Baz bar' foo'++data Frob = Frob Integer+ deriving (Show, Eq, Typeable, Data)++frob = Frob 2+frob' = Frob 1