diff --git a/peakachu.cabal b/peakachu.cabal
--- a/peakachu.cabal
+++ b/peakachu.cabal
@@ -1,5 +1,5 @@
 Name:                peakachu
-Version:             0.2
+Version:             0.3.0
 Category:            FRP
 Synopsis:            Experiemental library for composable interactive programs
 Description:
@@ -18,11 +18,12 @@
   hs-Source-Dirs:      src
   Extensions:
   Build-Depends:       base >= 3 && < 5, template-haskell,
-                       TypeCompose, derive,
-                       GLUT, time
+                       List >= 0.4.0 && < 0.6.0, TypeCompose >= 0.7 && < 0.8, derive >= 2.3,
+                       GLUT >= 2.0 && < 3.0, time
   Exposed-modules:     FRP.Peakachu
                        FRP.Peakachu.Program
                        FRP.Peakachu.Backend
+                       FRP.Peakachu.Backend.Internal
                        FRP.Peakachu.Backend.File
                        FRP.Peakachu.Backend.GLUT
                        FRP.Peakachu.Backend.GLUT.Getters
@@ -31,7 +32,6 @@
                        Control.Concurrent.MVar.YC
                        Control.FilterCategory
                        Data.ADT.Getters
-                       Data.Bijection.YC
                        Data.Newtype
   ghc-options:         -O2 -Wall
 
diff --git a/src/Control/Concurrent/MVar/YC.hs b/src/Control/Concurrent/MVar/YC.hs
--- a/src/Control/Concurrent/MVar/YC.hs
+++ b/src/Control/Concurrent/MVar/YC.hs
@@ -1,6 +1,6 @@
 module Control.Concurrent.MVar.YC (
-  modifyMVarPure, writeMVar
-  ) where
+    modifyMVarPure, writeMVar
+    ) where
 
 import Control.Applicative ()
 import Control.Concurrent.MVar (MVar, modifyMVar_)
diff --git a/src/Control/FilterCategory.hs b/src/Control/FilterCategory.hs
--- a/src/Control/FilterCategory.hs
+++ b/src/Control/FilterCategory.hs
@@ -3,9 +3,9 @@
 -- In Peakachu, both Program and Backend are instances of FilterCategory.
 
 module Control.FilterCategory
-  ( FilterCategory(..)
-  , genericFlattenC, mapMaybeC, filterC
-  ) where
+    ( FilterCategory(..)
+    , genericFlattenC, mapMaybeC, filterC
+    ) where
 
 import Control.Category (Category(..))
 import Control.Monad (guard)
@@ -14,8 +14,8 @@
 import Prelude hiding ((.), id)
 
 class Category cat => FilterCategory cat where
-  flattenC :: cat [a] a
-  arrC :: (a -> b) -> cat a b
+    flattenC :: cat [a] a
+    arrC :: (a -> b) -> cat a b
 
 genericFlattenC :: (FilterCategory cat, Foldable f) => cat (f a) a
 genericFlattenC = flattenC . arrC toList
@@ -25,9 +25,9 @@
 
 filterC :: FilterCategory cat => (a -> Bool) -> cat a a
 filterC cond =
-  mapMaybeC f
-  where
-    f x = do
-      guard $ cond x
-      return x
+    mapMaybeC f
+    where
+        f x = do
+            guard $ cond x
+            return x
 
diff --git a/src/Data/ADT/Getters.hs b/src/Data/ADT/Getters.hs
--- a/src/Data/ADT/Getters.hs
+++ b/src/Data/ADT/Getters.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE CPP #-}
+
 -- | ADT getters generation with Template Haskell
 --
 -- Example:
@@ -20,41 +22,53 @@
 -- etc.
 
 module Data.ADT.Getters
-  ( mkADTGetters
-  ) where
+    ( mkADTGetters
+    ) where
 
 import Language.Haskell.TH.Syntax
 
 mkADTGetters :: Name -> Q [Dec]
 mkADTGetters typeName = do
-  TyConI (DataD _ _ typeVars constructors _) <- reify typeName
-  return $ constructors >>= mkADTGetterFunc typeName typeVars
+    TyConI (DataD _ _ typeVars constructors _) <- reify typeName
+    return $ constructors >>= mkADTGetterFunc typeName typeVars
 
-mkADTGetterFunc :: Name -> [Name] -> Con -> [Dec]
+#if !(MIN_VERSION_template_haskell(2,4,0))
+type TyVarBndr = Name
+#endif
+
+tyVarBndrName :: TyVarBndr -> Name
+#if MIN_VERSION_template_haskell(2,4,0)
+tyVarBndrName (PlainTV name) = name
+tyVarBndrName (KindedTV name _) = name
+#else
+tyVarBndrName = id
+#endif
+
+mkADTGetterFunc :: Name -> [TyVarBndr] -> Con -> [Dec]
 mkADTGetterFunc typeName typeVars constructor =
-  [ SigD resName
-    . ForallT typeVars []
-    . AppT (AppT ArrowT (foldl AppT (ConT typeName) (map VarT typeVars)))
-    . AppT (ConT (mkName "Maybe"))
-    $ case containedTypes of
-    [] -> TupleT 0
-    [x] -> x
-    xs -> foldl AppT (TupleT (length xs)) xs
-  , FunD resName
-    [ Clause [ConP name (map VarP varNames)] clauseJust []
-    , Clause [WildP] clauseNothing []
+    [ SigD resName
+        . ForallT typeVars []
+        . AppT (AppT ArrowT (foldl AppT (ConT typeName) (map (VarT . tyVarBndrName) typeVars)))
+        . AppT (ConT (mkName "Maybe"))
+        $ case containedTypes of
+        [] -> TupleT 0
+        [x] -> x
+        xs -> foldl AppT (TupleT (length xs)) xs
+    , FunD resName
+        [ Clause [ConP name (map VarP varNames)] clauseJust []
+        , Clause [WildP] clauseNothing []
+        ]
     ]
-  ]
-  where
-    NormalC name params = constructor
-    containedTypes = map snd params
-    resName = mkName $ 'g' : nameBase name
-    varNames = map (mkName . ('x' :) . show) [0 .. length params - 1]
-    clauseJust =
-      NormalB . AppE (ConE (mkName "Just"))
-      $ case varNames of
-      [] -> TupE []
-      [x] -> VarE x
-      xs -> TupE (map VarE xs)
-    clauseNothing = NormalB . ConE . mkName $ "Nothing"
+    where
+        NormalC name params = constructor
+        containedTypes = map snd params
+        resName = mkName $ 'g' : nameBase name
+        varNames = map (mkName . ('x' :) . show) [0 .. length params - 1]
+        clauseJust =
+            NormalB . AppE (ConE (mkName "Just"))
+            $ case varNames of
+            [] -> TupE []
+            [x] -> VarE x
+            xs -> TupE (map VarE xs)
+        clauseNothing = NormalB . ConE . mkName $ "Nothing"
 
diff --git a/src/Data/Bijection/YC.hs b/src/Data/Bijection/YC.hs
deleted file mode 100644
--- a/src/Data/Bijection/YC.hs
+++ /dev/null
@@ -1,34 +0,0 @@
--- | In/with functions for bijective functions
---
--- Example:
---
--- > import Data.Bijection (Bijection(..), bimap)
--- > import Data.Bijection.YC (withBi2)
--- > import Data.Monoid (Monoid(..), Sum(..))
--- >
--- > biSum :: Num a => Bijection (->) a (Sum a)
--- > biSum = Bi Sum getSum
--- >
--- > > withBi2 (bimap biSum) mappend (Just 5) (Just 7)
--- > Just 12
--- >
--- > > withBi2 (bimap biSum) mappend Nothing (Just 7)
--- > Just 7
-
-module Data.Bijection.YC
-  ( withBi, inBi2, withBi2
-  ) where
-
-import Control.Arrow (Arrow)
-import Data.Bijection (Bijection(..), inBi, inverse)
-
-withBi :: Arrow x => Bijection x a b -> x b b -> x a a
-withBi = inBi . inverse
-
-inBi2 :: Bijection (->) a b -> (a -> a -> a) -> b -> b -> b
-inBi2 bi func left right =
-  biTo bi $ func (biFrom bi left) (biFrom bi right)
-
-withBi2 :: Bijection (->) a b -> (b -> b -> b) -> a -> a -> a
-withBi2 = inBi2 . inverse
-
diff --git a/src/Data/Newtype.hs b/src/Data/Newtype.hs
--- a/src/Data/Newtype.hs
+++ b/src/Data/Newtype.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE CPP #-}
+
 -- | In/with newtype functions generation with Template Haskell.
 --
 -- Example:
@@ -12,8 +14,8 @@
 -- > [9, 21]
 
 module Data.Newtype
-  ( mkInNewtypeFuncs, mkWithNewtypeFuncs
-  ) where
+    ( mkInNewtypeFuncs, mkWithNewtypeFuncs
+    ) where
 
 import Control.Applicative ((<$>), (<*>))
 import Language.Haskell.TH.Syntax
@@ -21,75 +23,104 @@
 nameAddSuf :: String -> Name -> Name
 nameAddSuf suf name = mkName (nameBase name ++ suf)
 
+#if !(MIN_VERSION_template_haskell(2,4,0))
+type TyVarBndr = Name
+type Pred = Type
+#endif
+
+tyVarBndrAddSuf :: String -> TyVarBndr -> TyVarBndr
+#if MIN_VERSION_template_haskell(2,4,0)
+tyVarBndrAddSuf suf (PlainTV name) = PlainTV (nameAddSuf suf name)
+tyVarBndrAddSuf suf (KindedTV name kind) = KindedTV (nameAddSuf suf name) kind
+#else
+tyVarBndrAddSuf = nameAddSuf
+#endif
+
+predAddSuf :: String -> Pred -> Pred
+#if MIN_VERSION_template_haskell(2,4,0)
+predAddSuf suf (ClassP name types) = ClassP name (map (typeAddSuf suf) types)
+predAddSuf suf (EqualP a b) = EqualP (typeAddSuf suf a) (typeAddSuf suf b)
+#else
+predAddSuf = typeAddSuf
+#endif
+
+tyVarBndrName :: TyVarBndr -> Name
+#if MIN_VERSION_template_haskell(2,4,0)
+tyVarBndrName (PlainTV name) = name
+tyVarBndrName (KindedTV name _) = name
+#else
+tyVarBndrName = id
+#endif
+
 typeAddSuf :: String -> Type -> Type
 typeAddSuf suf (ForallT names cxt typ) =
-  ForallT
-    (nameAddSuf suf <$> names)
-    (typeAddSuf suf <$> cxt)
-    (typeAddSuf suf typ)
+    ForallT
+        (tyVarBndrAddSuf suf <$> names)
+        (predAddSuf suf <$> cxt)
+        (typeAddSuf suf typ)
 typeAddSuf suf (VarT name) = VarT (nameAddSuf suf name)
 typeAddSuf suf (AppT left right) =
-  AppT (typeAddSuf suf left) (typeAddSuf suf right)
+    AppT (typeAddSuf suf left) (typeAddSuf suf right)
 typeAddSuf _ x = x
 
 data NewtypeFunc = In | With
 
 mkInNewtypeFuncs :: [Int] -> Name -> Q [Dec]
 mkInNewtypeFuncs idx typeName = do
-  info <- reify typeName
-  return $ idx >>= mkNewTypeFunc info In
+    info <- reify typeName
+    return $ idx >>= mkNewTypeFunc info In
 
 mkWithNewtypeFuncs :: [Int] -> Name -> Q [Dec]
 mkWithNewtypeFuncs idx typeName = do
-  info <- reify typeName
-  return $ idx >>= mkNewTypeFunc info With
+    info <- reify typeName
+    return $ idx >>= mkNewTypeFunc info With
 
 mkNewTypeFunc :: Info -> NewtypeFunc -> Int -> [Dec]
 mkNewTypeFunc info whatFunc funcIdx =
-  [ SigD resName
-    . ForallT
-      ( nameAddSuf <$> typeSuffixes <*> typeVars )
-      ( typeAddSuf <$> typeSuffixes <*> context )
-    . AppT (AppT ArrowT (mkFuncType inputType))
-    $ mkFuncType outputType
-  , FunD resName [clause]
-  ]
-  where
-    TyConI newtypeDef = info
-    NewtypeD context typeName typeVars constructor _ = newtypeDef
-    (consName, inType) =
-      case constructor of
-        NormalC c [(_, i)] -> (c, i)
-        RecC c [(_, _, i)] -> (c, i)
-        _ -> undefined
-    fName = mkName "f"
-    xNames = mkName . ('x' :) . show <$> [0 .. funcIdx - 1]
-    resName = mkName $ prefix ++ nameBase typeName ++ show funcIdx
-    typeSuffixes = show <$> [0 .. funcIdx]
-    fullType = foldl AppT (ConT typeName) (map VarT typeVars)
-    mkFuncType base =
-      foldr AppT (typeAddSuf (last typeSuffixes) base)
-      $ AppT ArrowT . (`typeAddSuf` base) <$> init typeSuffixes
-    withResName = mkName "res"
-    (prefix, inputType, outputType, clause) =
-      case whatFunc of
-        In ->
-          ( "in", inType, fullType
-          , Clause (VarP fName : (ConP consName . return . VarP <$> xNames))
-            ( NormalB
-            . AppE (ConE consName)
-            . foldl AppE (VarE fName)
-            $ VarE <$> xNames
-            ) []
-          )
-        With ->
-          ( "with", fullType, inType
-          , Clause (VarP <$> fName : xNames)
-            (NormalB (VarE withResName))
-            [ ValD (ConP consName [VarP withResName])
-              ( NormalB
-              . foldl AppE (VarE fName)
-              $ AppE (ConE consName) . VarE <$> xNames
-              ) []
-            ]
-          )
+    [ SigD resName
+        . ForallT
+            ( tyVarBndrAddSuf <$> typeSuffixes <*> typeVars )
+            ( predAddSuf <$> typeSuffixes <*> context )
+        . AppT (AppT ArrowT (mkFuncType inputType))
+        $ mkFuncType outputType
+    , FunD resName [clause]
+    ]
+    where
+        TyConI newtypeDef = info
+        NewtypeD context typeName typeVars constructor _ = newtypeDef
+        (consName, inType) =
+            case constructor of
+                NormalC c [(_, i)] -> (c, i)
+                RecC c [(_, _, i)] -> (c, i)
+                _ -> undefined
+        fName = mkName "f"
+        xNames = mkName . ('x' :) . show <$> [0 .. funcIdx - 1]
+        resName = mkName $ prefix ++ nameBase typeName ++ show funcIdx
+        typeSuffixes = show <$> [0 .. funcIdx]
+        fullType = foldl AppT (ConT typeName) (map (VarT . tyVarBndrName) typeVars)
+        mkFuncType base =
+            foldr AppT (typeAddSuf (last typeSuffixes) base)
+            $ AppT ArrowT . (`typeAddSuf` base) <$> init typeSuffixes
+        withResName = mkName "res"
+        (prefix, inputType, outputType, clause) =
+            case whatFunc of
+                In ->
+                    ( "in", inType, fullType
+                    , Clause (VarP fName : (ConP consName . return . VarP <$> xNames))
+                        ( NormalB
+                        . AppE (ConE consName)
+                        . foldl AppE (VarE fName)
+                        $ VarE <$> xNames
+                        ) []
+                    )
+                With ->
+                    ( "with", fullType, inType
+                    , Clause (VarP <$> fName : xNames)
+                        (NormalB (VarE withResName))
+                        [ ValD (ConP consName [VarP withResName])
+                            ( NormalB
+                            . foldl AppE (VarE fName)
+                            $ AppE (ConE consName) . VarE <$> xNames
+                            ) []
+                        ]
+                    )
diff --git a/src/FRP/Peakachu.hs b/src/FRP/Peakachu.hs
--- a/src/FRP/Peakachu.hs
+++ b/src/FRP/Peakachu.hs
@@ -1,60 +1,75 @@
 module FRP.Peakachu
-  ( runProgram
-  ) where
+    ( processList, processListV, runProgram
+    ) where
 
-import FRP.Peakachu.Backend (Backend(..), Sink(..))
-import FRP.Peakachu.Program (Program(..))
+import FRP.Peakachu.Backend (Backend (..))
+import FRP.Peakachu.Backend.Internal (Sink (..), MainLoop (..), ParallelIO (..))
+import FRP.Peakachu.Program (Program (..))
 import Control.Concurrent.MVar.YC (writeMVar)
 
 import Control.Concurrent (forkIO, threadDelay)
 import Control.Concurrent.MVar (newMVar, putMVar, readMVar, takeMVar)
-import Control.Monad (when)
-import Data.Function (fix)
+import Control.Monad (liftM, when)
+import Control.Monad.Trans.List.Funcs (repeatM)
+import Data.List.Class (List, concat, execute, scanl, takeWhile)
 import Data.Maybe (isNothing)
+import Prelude hiding (concat, scanl, takeWhile)
 
+-- | "Verbose" version of 'processList'.
+--
+-- The program's outputs after each input are grouped together
+processListV :: List l => Program a b -> l a -> l [b]
+processListV program
+    = liftM (progVals . snd) . takeWhile fst . scanl step (True, program)
+    where
+        step (_, Program _ Nothing) _ = (False, Program [] Nothing)
+        step (_, Program _ (Just more)) x = (True, more x)
+
+processList :: List l => Program a b -> l a -> l b
+processList program = concat . processListV program
+
 doWhile :: Monad m => m Bool -> m ()
-doWhile x = fix $ (x >>=) . flip when
+doWhile = execute . takeWhile id . repeatM
 
 runProgram :: Backend o i -> Program i o -> IO ()
 runProgram backend program = do
-  progVar <- newMVar program
-  resumeVar <- newMVar True
-  sinkVar <- newMVar Nothing
-  let
-    consumeOutput =
-      doWhile $ do
-        Just sink <- readMVar sinkVar
-        prog@(Program vals more) <- takeMVar progVar
-        case vals of
-          [] -> do
-            putMVar progVar prog
-            when (isNothing (progMore prog)) $ do
-              sinkQuitLoop sink
-              writeMVar resumeVar False
-            return False
-          (x : xs) -> do
-            putMVar progVar $ Program xs more
-            sinkConsume sink x
-            return True
-    handleInput val = do
-      prog@(Program vals maybeMore) <- takeMVar progVar
-      case maybeMore of
+    progVar <- newMVar program
+    resumeVar <- newMVar True
+    sinkVar <- newMVar Nothing
+    let
+        consumeOutput =
+            doWhile $ do
+                Just sink <- readMVar sinkVar
+                prog@(Program vals more) <- takeMVar progVar
+                case vals of
+                    [] -> do
+                        putMVar progVar prog
+                        when (isNothing (progMore prog)) $ do
+                            mlQuit $ sinkMainLoop sink
+                            writeMVar resumeVar False
+                        return False
+                    (x : xs) -> do
+                        putMVar progVar $ Program xs more
+                        sinkConsume sink x
+                        return True
+        handleInput val = do
+            prog@(Program vals maybeMore) <- takeMVar progVar
+            case maybeMore of
+                Nothing ->
+                    putMVar progVar prog
+                Just more -> do
+                    let Program mVals mMore = more val
+                    putMVar progVar $ Program (vals ++ mVals) mMore
+                    consumeOutput
+    sink <- runBackend backend handleInput
+    writeMVar sinkVar (Just sink)
+    mlInit $ sinkMainLoop sink
+    _ <- forkIO $ do
+        threadDelay 300000
+        consumeOutput
+    case mlRun (sinkMainLoop sink) of
         Nothing ->
-          putMVar progVar prog
-        Just more -> do
-          let Program mVals mMore = more val
-          putMVar progVar $ Program (vals ++ mVals) mMore
-          consumeOutput
-  sink <- runBackend backend handleInput
-  writeMVar sinkVar (Just sink)
-  sinkInit sink
-  forkIO $ do
-    threadDelay 300000
-    consumeOutput
-  case sinkMainLoop sink of
-    Nothing ->
-      doWhile $ do
-        threadDelay 200000 -- 0.2 sec
-        readMVar resumeVar
-    Just mainloop -> mainloop
-
+            doWhile $ do
+                threadDelay 200000 -- 0.2 sec
+                readMVar resumeVar
+        Just mainloop -> runParIO mainloop
diff --git a/src/FRP/Peakachu/Backend.hs b/src/FRP/Peakachu/Backend.hs
--- a/src/FRP/Peakachu/Backend.hs
+++ b/src/FRP/Peakachu/Backend.hs
@@ -1,83 +1,40 @@
-{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving, TemplateHaskell #-}
 
 module FRP.Peakachu.Backend
-  ( Backend(..), Sink(..)
-  ) where
+    ( Backend(..)
+    ) where
 
 import Control.FilterCategory (FilterCategory(..))
-import Data.Newtype (mkInNewtypeFuncs)
+import FRP.Peakachu.Backend.Internal (Sink(..))
 
 import Control.Category (Category(..))
-import Control.Concurrent (forkIO)
-import Control.Monad (liftM2)
-import Data.Generics.Aliases (orElse)
-import Data.Function (on)
+import Control.Instances () -- IO Monoids
+import Data.DeriveTH (derive, makeFunctor)
 import Data.Monoid (Monoid(..))
 
 import Prelude hiding ((.), id)
 
-data Sink a = Sink
-  { sinkConsume :: a -> IO ()
-  , sinkInit :: IO ()
-  , sinkMainLoop :: Maybe (IO ())
-  , sinkQuitLoop :: IO ()
-  }
-
-combineMainLoops :: Maybe (IO ()) -> Maybe (IO ()) -> Maybe (IO ())
-combineMainLoops (Just x) (Just y) = Just $ forkIO x >> y
-combineMainLoops x y = orElse x y
-
-instance Monoid (Sink a) where
-  mempty = Sink (const (return ())) (return ()) Nothing (return ())
-  mappend a b =
-    Sink
-    { sinkConsume = on (liftM2 (>>)) sinkConsume a b
-    , sinkInit = on (>>) sinkInit a b
-    , sinkMainLoop = on combineMainLoops sinkMainLoop a b
-    , sinkQuitLoop = on (>>) sinkQuitLoop a b
-    }
-
 newtype Backend progToBack backToProg =
-  Backend
-  { runBackend :: (backToProg -> IO ()) -> IO (Sink progToBack)
-  } -- if Monoid m => Monoid (IO m)
-  -- then could use GeneralizedNewtypeDeriving for Monoid
-
-$(mkInNewtypeFuncs [1,2] ''Backend)
-
-instance Monoid (Backend p2b b2p) where
-  mempty = Backend . return . return $ mempty
-  mappend = inBackend2 . liftM2 . liftM2 $ mappend
-
-instance Functor (Backend p2b) where
-  fmap =
-    inBackend1 . arg . arg
-    where
-      arg = flip (.)
+    Backend
+    { runBackend :: (backToProg -> IO ()) -> IO (Sink progToBack)
+    } deriving Monoid
+$(derive makeFunctor ''Backend)
 
 instance Category Backend where
-  id =
-    Backend f
-    where
-      f handler =
-        return mempty { sinkConsume = handler }
-  Backend left . Backend right =
-    Backend f
-    where
-      f handler = do
-        sinkLeft <- left handler
-        sinkRight <- right . sinkConsume $ sinkLeft
-        return sinkRight
-          { sinkInit =
-              sinkInit sinkLeft >> sinkInit sinkRight
-          , sinkMainLoop =
-              combineMainLoops
-              (sinkMainLoop sinkLeft) (sinkMainLoop sinkRight)
-          , sinkQuitLoop =
-              sinkQuitLoop sinkLeft >> sinkQuitLoop sinkRight
-          }
+    id =
+        Backend f
+        where
+            f handler =
+                return mempty { sinkConsume = handler }
+    Backend left . Backend right =
+        Backend f
+        where
+            f handler = do
+                sinkLeft <- left handler
+                sinkRight <- right . sinkConsume $ sinkLeft
+                return . Sink (sinkConsume sinkRight) $ mappend (sinkMainLoop sinkLeft) (sinkMainLoop sinkRight)
 
 instance FilterCategory Backend where
-  flattenC = Backend (runBackend id . mapM_)
-  arrC = (`fmap` id)
+    flattenC = Backend (runBackend id . mapM_)
+    arrC = (`fmap` id)
 
diff --git a/src/FRP/Peakachu/Backend/File.hs b/src/FRP/Peakachu/Backend/File.hs
--- a/src/FRP/Peakachu/Backend/File.hs
+++ b/src/FRP/Peakachu/Backend/File.hs
@@ -1,12 +1,13 @@
 {-# LANGUAGE TemplateHaskell #-}
 
 module FRP.Peakachu.Backend.File
-  ( FileToProgram(..), ProgramToFile(..), fileB
-  , gFileData, gFileError
-  ) where
+    ( FileToProgram(..), ProgramToFile(..), fileB
+    , gFileData, gFileError
+    ) where
 
 import Data.ADT.Getters (mkADTGetters)
-import FRP.Peakachu.Backend (Backend(..), Sink(..))
+import FRP.Peakachu.Backend (Backend(..))
+import FRP.Peakachu.Backend.Internal (Sink(..))
 
 import Control.Monad (join)
 import Data.Function (fix)
@@ -15,45 +16,45 @@
 import System.IO.Error (try, isEOFError)
 
 data FileToProgram a
-  = FileData String a
-  | FileError a
+    = FileData String a
+    | FileError a
 $(mkADTGetters ''FileToProgram)
 
 data ProgramToFile a
-  = ReadFile FilePath a
-  | WriteFile FilePath String a
+    = ReadFile FilePath a
+    | WriteFile FilePath String a
 
 maybeIO :: (IOError -> Bool) -> IO a -> IO (Maybe a)
 maybeIO isExpected =
-  join . fmap f . try
-  where
-    f (Right x) = return $ Just x
-    f (Left err)
-      | isExpected err = return Nothing
-      | otherwise = ioError err
+    join . fmap f . try
+    where
+        f (Right x) = return $ Just x
+        f (Left err)
+            | isExpected err = return Nothing
+            | otherwise = ioError err
 
 -- Lazy IO forbidden because imho it is horrible
 strictReadFile :: FilePath -> IO String
 strictReadFile filename = do
-  file <- openFile filename ReadMode
-  contents <- fix $ \rest -> do
-    mc <- maybeIO isEOFError $ hGetChar file
-    case mc of
-      Nothing -> return ""
-      Just c -> fmap (c :) rest
-  hClose file
-  return contents
+    file <- openFile filename ReadMode
+    contents <- fix $ \rest -> do
+        mc <- maybeIO isEOFError $ hGetChar file
+        case mc of
+            Nothing -> return ""
+            Just c -> fmap (c :) rest
+    hClose file
+    return contents
 
 fileB :: Backend (ProgramToFile a) (FileToProgram a)
 fileB =
-  Backend f
-  where
-    f handler =
-      return mempty { sinkConsume = consume }
-      where
-        consume (ReadFile filename tag) =
-          strictReadFile filename >>=
-          handler . (`FileData` tag)
-        consume (WriteFile filename contents _) =
-          writeFile filename contents
+    Backend f
+    where
+        f handler =
+            return mempty { sinkConsume = consume }
+            where
+                consume (ReadFile filename tag) =
+                    strictReadFile filename >>=
+                    handler . (`FileData` tag)
+                consume (WriteFile filename contents _) =
+                    writeFile filename contents
 
diff --git a/src/FRP/Peakachu/Backend/GLUT.hs b/src/FRP/Peakachu/Backend/GLUT.hs
--- a/src/FRP/Peakachu/Backend/GLUT.hs
+++ b/src/FRP/Peakachu/Backend/GLUT.hs
@@ -1,97 +1,105 @@
 {-# LANGUAGE TemplateHaskell #-}
 
 module FRP.Peakachu.Backend.GLUT
-  ( GlutToProgram(..), Image(..), ProgramToGlut(..), glut
-  , gIdleEvent, gTimerEvent, gMouseMotionEvent
-  , gKeyboardMouseEvent
-  ) where
+    ( GlutToProgram(..), Image(..), ProgramToGlut(..), glut
+    , gIdleEvent, gTimerEvent, gMouseMotionEvent
+    , gKeyboardMouseEvent
+    ) where
 
 import Control.Concurrent.MVar.YC (modifyMVarPure)
 import Data.ADT.Getters (mkADTGetters)
-import FRP.Peakachu.Backend (Backend(..), Sink(..))
+import FRP.Peakachu.Backend (Backend(..))
+import FRP.Peakachu.Backend.Internal
+    (Sink(..), MainLoop(..), ParallelIO(..))
 
-import Control.Concurrent.MVar (newMVar, putMVar, takeMVar)
+import Control.Concurrent.MVar (MVar, newMVar, putMVar, takeMVar)
 import Data.Monoid (Monoid(..))
 import Graphics.UI.GLUT
-  ( GLfloat, ($=), ($~), get
-  , ClearBuffer(..), Key(..), KeyState(..)
-  , Modifiers, Position(..), Size(..), Timeout
-  , DisplayMode(..), initialDisplayMode, swapBuffers
-  , createWindow, getArgsAndInitialize
-  , displayCallback, idleCallback
-  , keyboardMouseCallback
-  , motionCallback, passiveMotionCallback
-  , windowSize, addTimerCallback
-  , clear, flush, mainLoop, leaveMainLoop
-  )
+    ( GLfloat, ($=), ($~), get
+    , ClearBuffer(..), Key(..), KeyState(..)
+    , Modifiers, Position(..), Size(..), Timeout
+    , DisplayMode(..), initialDisplayMode, swapBuffers
+    , createWindow, getArgsAndInitialize
+    , displayCallback, idleCallback
+    , keyboardMouseCallback
+    , motionCallback, passiveMotionCallback
+    , windowSize, addTimerCallback
+    , clear, flush, mainLoop, leaveMainLoop
+    )
 
 data Image = Image { runImage :: IO ()}
 
 instance Monoid Image where
-  mempty = Image $ return ()
-  mappend (Image a) (Image b) = Image $ a >> b
+    mempty = Image $ return ()
+    mappend (Image a) (Image b) = Image $ a >> b
 
 data GlutToProgram a
-  = IdleEvent
-  | TimerEvent a
-  | MouseMotionEvent GLfloat GLfloat
-  | KeyboardMouseEvent Key KeyState Modifiers Position
+    = IdleEvent
+    | TimerEvent a
+    | MouseMotionEvent GLfloat GLfloat
+    | KeyboardMouseEvent Key KeyState Modifiers Position
 $(mkADTGetters ''GlutToProgram)
 
 data ProgramToGlut a
-  = DrawImage Image
-  | SetTimer Timeout a
+    = DrawImage Image
+    | SetTimer Timeout a
 
-glut :: Backend (ProgramToGlut a) (GlutToProgram a)
-glut =
-  Backend b
-  where
-    b handler = do
-      _ <- getArgsAndInitialize
-      initialDisplayMode $~ (DoubleBuffered:)
-      createWindow "test"
-      displayCallback $= return ()
-      -- all the OpenGL drawing must be performed from the same thread
-      -- that runs the GLUT event-loop.
-      -- so instead of consuming when given input, we add it to the todo-list.
-      -- the next time any GLUT event comes (should be immediate),
-      -- we consume all the todo-list.
-      -- without this mechanism the graphics flickers.
-      todoVar <- newMVar []
-      let
-        consume (DrawImage image) = do
-          clear [ ColorBuffer ]
-          runImage image
-          swapBuffers
-          flush
-        consume (SetTimer timeout tag) =
-          -- there seems to be a bug with addTimerCallback.
-          -- sometimes it calls you back straight away..
-          -- but doing a work around with an io-thread seems
-          -- to be very slow
-          addTimerCallback timeout . handler . TimerEvent $ tag
+glutConsume :: (GlutToProgram a -> IO ()) -> ProgramToGlut a -> IO ()
+glutConsume _ (DrawImage image) = do
+    clear [ ColorBuffer ]
+    runImage image
+    swapBuffers
+    flush
+glutConsume handler (SetTimer timeout tag) =
+    -- there seems to be a bug with addTimerCallback.
+    -- sometimes it calls you back straight away..
+    -- but doing a work around with an io-thread seems
+    -- to be very slow
+    addTimerCallback timeout . handler . TimerEvent $ tag
+
+setGlutCallbacks :: MVar [ProgramToGlut a] -> (GlutToProgram a -> IO ()) -> IO ()
+setGlutCallbacks todoVar handler = do
+    idleCallback $= Just (preHandler IdleEvent)
+    keyboardMouseCallback $=
+        Just (
+        (fmap . fmap . fmap . fmap)
+        preHandler KeyboardMouseEvent)
+    motionCallback $= Just motion
+    passiveMotionCallback $= Just motion
+    where
         preHandler event = do
-          todo <- takeMVar todoVar
-          putMVar todoVar []
-          mapM_ consume . reverse $ todo
-          handler event
-        setCallbacks = do
-          idleCallback $= Just (preHandler IdleEvent)
-          keyboardMouseCallback $=
-            Just (
-            (fmap . fmap . fmap . fmap)
-            preHandler KeyboardMouseEvent)
-          motionCallback $= Just motion
-          passiveMotionCallback $= Just motion
+            todo <- takeMVar todoVar
+            putMVar todoVar []
+            mapM_ (glutConsume handler) . reverse $ todo
+            handler event
         motion (Position px py) = do
-          Size sx sy <- get windowSize
-          preHandler $ MouseMotionEvent (p2g sx px) (- p2g sy py)
+            Size sx sy <- get windowSize
+            preHandler $ MouseMotionEvent (p2g sx px) (- p2g sy py)    
         p2g sa pa = 2 * fromIntegral pa / fromIntegral sa - 1
-      setCallbacks
-      return Sink
-        { sinkConsume = modifyMVarPure todoVar . (:)
-        , sinkInit = handler $ MouseMotionEvent 0 0
-        , sinkMainLoop = Just mainLoop
-        , sinkQuitLoop = leaveMainLoop
-        }
 
+glut :: Backend (ProgramToGlut a) (GlutToProgram a)
+glut =
+    Backend b
+    where
+        b handler = do
+            _ <- getArgsAndInitialize
+            initialDisplayMode $~ (DoubleBuffered:)
+            _ <- createWindow "test"
+            displayCallback $= return ()
+            -- all the OpenGL drawing must be performed from the same thread
+            -- that runs the GLUT event-loop.
+            -- so instead of consuming when given input, we add it to the todo-list.
+            -- the next time any GLUT event comes (should be immediate),
+            -- we consume all the todo-list.
+            -- without this mechanism the graphics flickers.
+            todoVar <- newMVar []
+            setGlutCallbacks todoVar handler
+            return Sink
+                { sinkConsume = modifyMVarPure todoVar . (:)
+                , sinkMainLoop =
+                    MainLoop
+                    { mlInit = handler $ MouseMotionEvent 0 0
+                    , mlQuit = leaveMainLoop
+                    , mlRun = Just $ ParIO mainLoop
+                    }
+                }
diff --git a/src/FRP/Peakachu/Backend/GLUT/Getters.hs b/src/FRP/Peakachu/Backend/GLUT/Getters.hs
--- a/src/FRP/Peakachu/Backend/GLUT/Getters.hs
+++ b/src/FRP/Peakachu/Backend/GLUT/Getters.hs
@@ -5,8 +5,8 @@
 {-# LANGUAGE TemplateHaskell #-}
 
 module FRP.Peakachu.Backend.GLUT.Getters
-  ( gChar, gMouseButton, gSpecialKey, gDown, gUp
-  ) where
+    ( gChar, gMouseButton, gSpecialKey, gDown, gUp
+    ) where
 
 import Data.ADT.Getters (mkADTGetters)
 
diff --git a/src/FRP/Peakachu/Backend/Internal.hs b/src/FRP/Peakachu/Backend/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/FRP/Peakachu/Backend/Internal.hs
@@ -0,0 +1,39 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module FRP.Peakachu.Backend.Internal
+    ( Sink(..), MainLoop(..), ParallelIO(..)
+    ) where
+
+import Data.Newtype (mkInNewtypeFuncs)
+
+import Control.Concurrent (forkIO)
+import Control.Instances () -- IO Monoids
+import Data.DeriveTH (derive, makeMonoid)
+import Data.Monoid
+
+newtype ParallelIO = ParIO { runParIO :: IO () }
+$(mkInNewtypeFuncs [1,2] ''ParallelIO)
+
+instance Monoid ParallelIO where
+    mempty = ParIO mempty
+    mappend a = inParallelIO2 (>>) (inParallelIO1 ((>> return ()) . forkIO) a)
+
+data MainLoop =
+    MainLoop
+    { mlInit :: IO ()
+    , mlQuit :: IO ()
+    , mlRun :: Maybe ParallelIO
+    }
+$(derive makeMonoid ''MainLoop)
+
+data Sink a = Sink
+    { sinkConsume :: a -> IO ()
+    , sinkMainLoop :: MainLoop
+    }
+
+-- not using "derive" to derive Sink's Monoid because it has a bug
+instance Monoid (Sink a) where
+    mempty = Sink mempty mempty
+    mappend (Sink x0 x1) (Sink y0 y1) =
+        Sink (mappend x0 y0) (mappend x1 y1)
+
diff --git a/src/FRP/Peakachu/Backend/StdIO.hs b/src/FRP/Peakachu/Backend/StdIO.hs
--- a/src/FRP/Peakachu/Backend/StdIO.hs
+++ b/src/FRP/Peakachu/Backend/StdIO.hs
@@ -1,19 +1,58 @@
 -- | A Peakachu backend to write output to the console
 
-module FRP.Peakachu.Backend.StdIO (stdoutB) where
+module FRP.Peakachu.Backend.StdIO
+    ( stdoutB, interactB
+    ) where
 
-import FRP.Peakachu.Backend (Backend(..), Sink(..))
+import FRP.Peakachu.Backend (Backend(..))
+import FRP.Peakachu.Backend.Internal (Sink(..), MainLoop(..), ParallelIO(..))
+import Control.Concurrent.MVar.YC (writeMVar)
 
+import Control.Concurrent.MVar
+    (newMVar, putMVar, readMVar, takeMVar)
+import Control.Monad (when)
 import Data.Monoid (mempty)
-import System.IO (hFlush, stdout)
+import System.IO (hFlush, hReady, stdin, stdout)
 
 stdoutB :: Backend String ()
 stdoutB =
-  Backend . const . return $ mempty
-  { sinkConsume = consume
-  }
-  where
-    consume x = do
-      putStr x
-      hFlush stdout
+    Backend . return . return $
+    mempty { sinkConsume = (>> hFlush stdout) . putStr }
+
+whileM :: Monad m => m Bool -> m () -> m ()
+whileM cond iter = do
+    resume <- cond
+    when resume $ do
+        iter
+        whileM cond iter
+
+-- | The Peakachu equivalent to 'interact'.
+-- Prints all output lines from the program, and feeds
+-- input lines from the user to the program.
+interactB :: Backend String String
+interactB =
+    Backend f
+    where
+        f handler = do
+            resumeVar <- newMVar True
+            lineVar <- newMVar ""
+            return Sink
+                { sinkConsume = putStrLn
+                , sinkMainLoop =
+                        mempty
+                        { mlQuit = writeMVar resumeVar False
+                        , mlRun =
+                                Just . ParIO . whileM (readMVar resumeVar) $ do
+                                    isReady <- hReady stdin
+                                    when isReady $ do
+                                        c <- getChar
+                                        prevLine <- takeMVar lineVar
+                                        case c of
+                                            '\n' -> do
+                                                _ <- handler prevLine
+                                                putMVar lineVar ""
+                                            _ ->
+                                                putMVar lineVar $ prevLine ++ [c]
+                        }
+                }
 
diff --git a/src/FRP/Peakachu/Backend/Time.hs b/src/FRP/Peakachu/Backend/Time.hs
--- a/src/FRP/Peakachu/Backend/Time.hs
+++ b/src/FRP/Peakachu/Backend/Time.hs
@@ -1,21 +1,22 @@
 -- | A Peakachu backend to get the time
 
 module FRP.Peakachu.Backend.Time
-  ( getTimeB
-  ) where
+    ( getTimeB
+    ) where
 
 import Data.Monoid (Monoid(..))
-import FRP.Peakachu.Backend (Backend(..), Sink(..))
+import FRP.Peakachu.Backend (Backend(..))
+import FRP.Peakachu.Backend.Internal (Sink(..))
 
 import Data.Time.Clock (UTCTime, getCurrentTime)
 
 getTimeB :: Backend a (UTCTime, a)
 getTimeB =
-  Backend f
-  where
-    f handler =
-      return mempty { sinkConsume = consume }
-      where
-        consume tag = do
-          now <- getCurrentTime
-          handler (now, tag)
+    Backend f
+    where
+        f handler =
+            return mempty { sinkConsume = consume }
+            where
+                consume tag = do
+                    now <- getCurrentTime
+                    handler (now, tag)
diff --git a/src/FRP/Peakachu/Program.hs b/src/FRP/Peakachu/Program.hs
--- a/src/FRP/Peakachu/Program.hs
+++ b/src/FRP/Peakachu/Program.hs
@@ -1,183 +1,194 @@
 {-# LANGUAGE GeneralizedNewtypeDeriving, TemplateHaskell #-}
 
+-- | @Program a b@ is a pure representation of a computer program,
+-- which accepts inputs of type @a@, and outputs values of type @b@.
+-- It may also terminate. It can output zero or more @b@ values after each @a@ input.
+--
+-- * A simple stateless input-output-loop can be created from a function
+--   with 'arrC'.
+--
+-- * A simple stateful input-output-loop can be created using 'scanlP'.
+--
+-- * Outputs can be filtered using 'filterC'.
+--
+-- Programs may also be composed together in several ways using common type-classes
+--
+-- * 'Category': @Program a b -> Program b c -> Program a c@. One program's outputs are fed
+--   to another program as input.
+--
+-- * 'Monoid': @Program a b -> Program a b -> Program a b@. Both programs run in parallel processing the same input. Resulting Program outputs both's outputs.
+--
+-- * 'Applicative': @Program a (b -> c) -> Program a b -> Program a c@.
+--
+-- * Alternative `MonadPlus`: 'AppendProgram' is a newtype wrapper whose `Monoid` instance runs one program after the other finishes (like `ZipList` offers an alternative `Applicative` instance for lists). It's also a `Monad` ant its monadic bind allows us to invoke inner programs based on an outer program's outputs.
+
 module FRP.Peakachu.Program
-  ( Program(..), MergeProgram(..), AppendProgram(..)
-  , ProgCat(..)
-  , singleValueP, lstP, lstPs, delayP
-  ) where
+    ( Program(..), AppendProgram(..)
+    , scanlP, emptyP, takeWhileP, loopbackP, singleValueP, lstP, lstPs, delayP
+    , withAppendProgram1, withAppendProgram2
+    ) where
 
 import Control.FilterCategory (FilterCategory(..), genericFlattenC)
 import Data.ADT.Getters (mkADTGetters)
-import Data.Bijection.YC (withBi2)
 import Data.Newtype (mkWithNewtypeFuncs)
 
 import Control.Applicative (Applicative(..), (<$>), liftA2)
 import Control.Category (Category(..))
 import Control.Monad (MonadPlus(..), ap)
-import Data.Bijection (Bijection(..), bimap)
-import Data.Generics.Aliases (orElse)
+import Data.DeriveTH (derive, makeFunctor)
 import Data.List (genericDrop, genericTake)
 import Data.Maybe (mapMaybe, catMaybes)
 import Data.Monoid (Monoid(..))
 
 import Prelude hiding ((.), id)
 
+-- | A computer program
 data Program a b = Program
-  { progVals :: [b]
-  , progMore :: Maybe (a -> Program a b)
-  }
-
-class FilterCategory prog => ProgCat prog where
-  scanlP :: (b -> a -> b) -> b -> prog a b
-  emptyP :: prog a b
-  takeWhileP :: (a -> Bool) -> prog a a
-  loopbackP :: prog a (Either a b) -> prog a b
-
-singleValueP :: ProgCat prog => prog a ()
-singleValueP = scanlP const () . emptyP
-
-delayP :: (Integral i, ProgCat prog) => i -> prog a a
-delayP n =
-  flattenC . arrC (genericDrop n) . scanlP step []
-  where
-    step xs = (: genericTake n xs)
+    { progVals :: [b]
+    , progMore :: Maybe (a -> Program a b)
+    }
+$(derive makeFunctor ''Program)
 
 instance Category Program where
-  id =
-    Program [] (Just f)
-    where
-      f x = Program [x] (Just f)
-  left . right =
-    Program (catMaybes stuff >>= progVals) more
-    where
-      Program rightStart rightMore = right
-      stuff = scanl step (Just left) rightStart
-      step l valRight = do
-        Program _ moreLeft <- l
-        moreFunc <- moreLeft
-        return $ moreFunc valRight
-      more = do
-        moreFunc <- rightMore
-        lastStuff <- last stuff
-        return $ (.) (Program [] (progMore lastStuff)) . moreFunc
-
-instance Functor (Program a) where
-  fmap f p =
-    Program
-    { progVals = fmap f . progVals $ p
-    , progMore = (fmap . fmap . fmap) f . progMore $ p
-    }
+    id =
+        Program [] (Just f)
+        where
+            f x = Program [x] (Just f)
+    left . right =
+        Program (catMaybes stuff >>= progVals) more
+        where
+            Program rightStart rightMore = right
+            stuff = scanl step (Just left) rightStart
+            step l valRight = do
+                Program _ moreLeft <- l
+                moreFunc <- moreLeft
+                return $ moreFunc valRight
+            more = do
+                moreFunc <- rightMore
+                lastStuff <- last stuff
+                return $ (.) (Program [] (progMore lastStuff)) . moreFunc
 
 instance FilterCategory Program where
-  flattenC =
-    f []
-    where
-      f = (`Program` Just f)
-  arrC = (<$> id)
+    flattenC =
+        f []
+        where
+            f = (`Program` Just f)
+    arrC = (<$> id)
 
 $(mkADTGetters ''Either)
 
-instance ProgCat Program where
-  emptyP = Program [] Nothing
-  scanlP step start =
-    Program [start] $ Just (scanlP step . step start)
-  takeWhileP cond =
+-- | Create a stateful input-output-loop from a simple function
+scanlP :: (b -> a -> b) -> b -> Program a b
+scanlP step start = Program [start] $ Just (scanlP step . step start)
+
+-- | A program that terminates immediately
+emptyP :: Program a b
+emptyP = Program [] Nothing
+
+-- | Terminate when a predicate on input fails
+takeWhileP :: (a -> Bool) -> Program a a
+takeWhileP cond =
     Program [] (Just f)
     where
-      f x
-        | cond x = Program [x] (Just f)
-        | otherwise = Program [] Nothing
-  loopbackP program =
+        f x
+            | cond x = Program [x] (Just f)
+            | otherwise = Program [] Nothing
+
+-- | Feed some outputs of a 'Program' to itself
+loopbackP :: Program a (Either a b) -> Program a b
+loopbackP program =
     Program
     { progVals = stuff >>= mapMaybe gRight . progVals
     , progMore = (fmap . fmap) loopbackP . progMore . last $ stuff
     }
     where
-      stuff =
-        scanl step program
-        . mapMaybe gLeft . progVals $ program
-      step prev val =
-        maybe emptyP ($ val) (progMore prev)
-
-newtype MergeProgram a b = MergeProg
-  { runMergeProg :: Program a b
-  } deriving (Category, FilterCategory, Functor, ProgCat)
+        stuff =
+            scanl step program
+            . mapMaybe gLeft . progVals $ program
+        step prev val =
+            maybe emptyP ($ val) (progMore prev)
 
-$(mkWithNewtypeFuncs [2] ''MergeProgram)
+-- | A program that outputs a value and immediately terminates
+singleValueP :: Program a ()
+singleValueP = scanlP const () . emptyP
 
-biMergeProg :: Bijection (->) (Program a b) (MergeProgram a b)
-biMergeProg = Bi MergeProg runMergeProg
+-- | Delay the outputs of a 'Program'
+delayP :: Integral i => i -> Program a a
+delayP n =
+    flattenC . arrC (genericDrop n) . scanlP step []
+    where
+        step xs = (: genericTake n xs)
 
-instance Monoid (MergeProgram a b) where
-  mempty = emptyP
-  mappend (MergeProg left) (MergeProg right) =
-    MergeProg Program
-    { progVals =
-      mappend (progVals left) (progVals right)
-    , progMore =
-      withBi2 ((bimap . bimap) biMergeProg)
-      mappend (progMore left) (progMore right)
-    }
+-- would be nice to derive this.
+-- but "derive" currently can't: http://code.google.com/p/ndmitchell/issues/detail?id=270&q=proj:Derive
+instance Monoid (Program a b) where
+    mempty = Program mempty mempty
+    mappend left right =
+        Program
+        { progVals = mappend (progVals left) (progVals right)
+        , progMore = mappend (progMore left) (progMore right)
+        }
 
-instance Applicative (MergeProgram a) where
-  pure x =
-    MergeProg Program
-    { progVals = pure x
-    , progMore = pure . pure . runMergeProg . pure $ x
-    }
-  MergeProg left <*> MergeProg right =
-    MergeProg Program
-    { progVals = progVals left <*> progVals right
-    , progMore =
-      (liftA2 . liftA2 . withMergeProgram2)
-      (<*>) (progMore left) (progMore right)
-    }
+instance Applicative (Program a) where
+    pure x =
+        Program
+        { progVals = pure x
+        , progMore = (pure . pure) (pure x)
+        }
+    left <*> right =
+        Program
+        { progVals = progVals left <*> progVals right
+        , progMore = (liftA2 . liftA2) (<*>) (progMore left) (progMore right)
+        }
 
+-- Combine programs to run in sequence
 newtype AppendProgram a b = AppendProg
-  { runAppendProg :: Program a b
-  } deriving (Category, FilterCategory, Functor, ProgCat)
+    { runAppendProg :: Program a b
+    } deriving (Category, FilterCategory, Functor)
 
 $(mkWithNewtypeFuncs [1,2] ''AppendProgram)
 
 instance Monoid (AppendProgram a b) where
-  mempty = emptyP
-  mappend (AppendProg left) (AppendProg right) =
-    AppendProg $
-    case progMore left of
-      Nothing -> Program
-        { progVals = progVals left ++ progVals right
-        , progMore = progMore right
-        }
-      Just more -> Program
-        { progVals = progVals left
-        , progMore = Just $ flip (withAppendProgram2 mappend) right <$> more
-        }
+    mempty = AppendProg emptyP
+    mappend (AppendProg left) (AppendProg right) =
+        AppendProg $
+        case progMore left of
+            Nothing -> Program
+                { progVals = progVals left ++ progVals right
+                , progMore = progMore right
+                }
+            Just more -> Program
+                { progVals = progVals left
+                , progMore = Just $ flip (withAppendProgram2 mappend) right <$> more
+                }
 
 instance Monad (AppendProgram a) where
-  return x = AppendProg $ Program [x] Nothing
-  AppendProg left >>= right =
-    mconcat $ map right (progVals left) ++ [rest]
-    where
-      rest =
-        AppendProg Program
-        { progVals = []
-        , progMore =
-          (fmap . fmap . withAppendProgram1)
-          (>>= right) (progMore left)
-        }
+    return x = AppendProg $ Program [x] Nothing
+    AppendProg left >>= right =
+        mconcat $ map right (progVals left) ++ [rest]
+        where
+            rest =
+                AppendProg Program
+                { progVals = []
+                , progMore =
+                    (fmap . fmap . withAppendProgram1)
+                    (>>= right) (progMore left)
+                }
 
 instance MonadPlus (AppendProgram a) where
-  mzero = mempty
-  mplus = mappend
+    mzero = mempty
+    mplus = mappend
 
 instance Applicative (AppendProgram a) where
-  pure = return
-  (<*>) = ap
+    pure = return
+    (<*>) = ap
 
-lstPs :: ProgCat prog => Maybe b -> (a -> Maybe b) -> prog a b
+-- | Given a partial function @(a -> Maybe b)@ and a start value, output its most recent result on an input.
+lstPs :: Maybe b -> (a -> Maybe b) -> Program a b
 lstPs start f =
-  genericFlattenC . scanlP (flip orElse) start . arrC f
+    genericFlattenC . scanlP (flip mplus) start . arrC f
 
-lstP :: ProgCat prog => (a -> Maybe b) -> prog a b
+-- | Given a partial function @(a -> Maybe b)@, output its most recent result on an input.
+lstP :: (a -> Maybe b) -> Program a b
 lstP = lstPs Nothing
 
