packages feed

Cascade (empty) → 0.1.0.0

raw patch · 11 files changed

+800/−0 lines, 11 filesdep +basedep +comonaddep +ghc-primsetup-changed

Dependencies added: base, comonad, ghc-prim, mtl, void

Files

+ Cascade.cabal view
@@ -0,0 +1,89 @@+-- Initial Cascade.cabal generated by cabal init.  For further +-- documentation, see http://haskell.org/cabal/users-guide/++-- The name of the package.+name:                Cascade++-- The package version.  See the Haskell package versioning policy (PVP) +-- for standards guiding when and how versions should be incremented.+-- http://www.haskell.org/haskellwiki/Package_versioning_policy+-- PVP summary:      +-+------- breaking API changes+--                   | | +----- non-breaking API additions+--                   | | | +--- code changes with no API change+version:             0.1.0.0++-- A short (one-line) description of the package.+synopsis:            Playing with reified categorical composition++-- A longer description of the package.+-- description:         ++-- URL for the project homepage or repository.+homepage:            http://github.com/rampion/Cascade++-- The license under which the package is released.+license:             PublicDomain++-- The file containing the license text.+license-file:        LICENSE++-- The package author(s).+author:              Noah Luck Easterly++-- An email address to which users can send suggestions, bug reports, and +-- patches.+maintainer:          noah.easterly@gmail.com++-- A copyright notice.+-- copyright:           ++category:            Control++build-type:          Simple++-- Extra files to be distributed with the package, such as examples or a +extra-source-files:  README.md++-- Constraint on the version of Cabal needed to build this package.+cabal-version:       >=1.10+++library+  -- Modules exported by the library.+  exposed-modules: Cascade+                 , Cascade.Debugger+                 , Cascade.Examples+                 , Cascade.Product+                 , Cascade.Sum+                 , Cascade.Util.ListKind+                 , Cascade.Operators+  +  -- Modules included in this library but not exported.+  -- other-modules:       +  +  -- LANGUAGE extensions used by modules in this package.+  other-extensions: KindSignatures+                  , TypeOperators+                  , DataKinds+                  , GADTs+                  , RankNTypes+                  , ConstraintKinds+                  , FlexibleContexts+                  , FlexibleInstances+                  , MultiParamTypeClasses+                  , PolyKinds+                  , TypeFamilies+                  , UndecidableInstances+  +  -- Other library packages from which modules are imported.+  build-depends: base >=4.7 && <4.8+               , comonad >=4.2 && <4.3+               , mtl >=2.2 && <2.3+               , ghc-prim >=0.3 && <0.4+               , void >= 0.6+  +  -- Directories containing source files.+  -- hs-source-dirs:      +  +  -- Base language which the package is written in.+  default-language:    Haskell2010
+ Cascade.hs view
@@ -0,0 +1,65 @@+{-# LANGUAGE KindSignatures         #-}+{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE GADTs                  #-}+{-# LANGUAGE PolyKinds              #-}+{-# LANGUAGE RankNTypes             #-}+module Cascade where+import Cascade.Util.ListKind (Last)++import Control.Arrow (Kleisli(..))+import Control.Category (Category(..), (>>>))+import Control.Comonad (Cokleisli(..), Comonad(..))+import Control.Monad.Identity (Identity(..))+import Prelude hiding (id, (.))++-- reified categorical composition+data CascadeC (c :: t -> t -> *) (ts :: [t]) where+  (:>>>)  :: c x y -> CascadeC c (y ': zs) -> CascadeC c (x ': y ': zs)+  Done    :: CascadeC c '[t]+infixr 1 :>>>++-- transform the underlying category used in a cascade+transform :: (forall a b. c a b -> c' a b) -> CascadeC c ts -> CascadeC c' ts+transform _ Done = Done+transform g (f :>>> fs) = g f :>>> transform g fs++-- compress into a function+cascade :: Category c => CascadeC c (t ': ts) -> c t (Last (t ': ts))+cascade Done = id+cascade (f :>>> fs) = f >>> cascade fs++-- specialize to functions+type Cascade   = CascadeC (->)++-- specialize to monads+type CascadeM m = CascadeC (Kleisli m)+(>=>:) :: (x -> m y) -> CascadeM m (y ': zs) -> CascadeM m (x ': y ': zs)+(>=>:) f cm = Kleisli f :>>> cm+infixr 1 >=>:++cascadeM :: Monad m => CascadeM m (t ': ts) -> t -> m (Last (t ': ts))+cascadeM = runKleisli . cascade++-- transform a simple cascade to and from a Kleisli cascade+unwrapM :: CascadeM Identity ts -> Cascade ts+unwrapM = transform $ \f -> runIdentity . runKleisli f++wrapM :: Monad m => Cascade ts -> CascadeM m ts+wrapM = transform $ \f -> Kleisli $ return . f++-- specialize to comonads+type CascadeW w = CascadeC (Cokleisli w)+(=>=:) :: (w x -> y) -> CascadeW w (y ': zs) -> CascadeW w (x ': y ': zs)+(=>=:) f cw = Cokleisli f :>>> cw+infixr 1 =>=:++cascadeW :: Comonad w => CascadeW w (t ': ts) -> w t -> Last (t ': ts)+cascadeW = runCokleisli . cascade++-- transform a simple cascade to and from a Cokleisli cascade+unwrapW :: CascadeW Identity ts -> Cascade ts+unwrapW = transform $ \f -> runCokleisli f . Identity++wrapW :: Comonad w => Cascade ts -> CascadeW w ts+wrapW = transform $ \f -> Cokleisli $ f . extract
+ Cascade/Debugger.hs view
@@ -0,0 +1,85 @@+{-# LANGUAGE ConstraintKinds        #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE GADTs                  #-}+{-# LANGUAGE TypeFamilies           #-}+{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE UndecidableInstances   #-} -- to use All Show zs+module Cascade.Debugger where+import Cascade+import Cascade.Util.ListKind++import Control.Arrow+import Control.Category+import Prelude hiding (id, (.))+import GHC.Prim         (Constraint)+++data DebuggerM (m :: * -> *) (past :: [*]) (current :: *) (future :: [*]) where++  Begin     :: (a -> m (DebuggerM m '[a] b cs))+            -> DebuggerM m '[] a (b ': cs)++  Break     :: (a -> m (DebuggerM m (a ': z ': ys) b cs)) +            -> DebuggerM m ys z (a ': b ': cs)+            -> z+            -> a +            -> DebuggerM m (z ': ys) a (b ': cs)++  End       :: DebuggerM m ys z '[a]+            -> z+            -> a+            -> DebuggerM m (z ': ys) a '[]++instance (All Show zs, All Show bs, Show a) => Show (DebuggerM m zs a bs) where+  showsPrec p d = case d of+      Begin _       -> showString "Begin" +      Break _ _ z a -> showParen (p > 10) $ showString "Break" . showIO z a+      End     _ z a -> showParen (p > 10) $ showString "End  " . showIO z a+    where showIO z a =  showString " { given = ".  showsPrec 11 z . +                        showString ", returned = " . showsPrec 11 a . +                        showString " }"++printHistory :: (All Show zs, All Show bs, Show a) => DebuggerM m zs a bs-> IO ()+printHistory d@(Begin _      ) = print d+printHistory d@(Break _ _ _ _) = print d >> printHistory (back d)+printHistory d@(End     _ _ _) = print d >> printHistory (back d)++given :: DebuggerM m (z ': ys) a bs -> z+given (Break _ _ z _) = z+given (End     _ z _) = z++returned :: DebuggerM m (z ': ys) a bs -> a+returned (Break _ _ _ a) = a+returned (End     _ _ a) = a++back :: DebuggerM m (z ': ys) a bs -> DebuggerM m ys z (a ': bs)+back (Break _ d _ _) = d+back (End     d _ _) = d++redo :: DebuggerM m (a ': z ': ys) b cs -> m (DebuggerM m (a ': z ': ys) b cs)+redo = step . back++redoWith :: a -> DebuggerM m (a ': zs) b cs -> m (DebuggerM m (a ': zs) b cs)+redoWith x = use x . back++use :: a -> DebuggerM m zs a (b ': cs) -> m (DebuggerM m (a ': zs) b cs)+use a (Begin f      ) = f a+use a (Break f _ _ _) = f a++step :: DebuggerM m (z ': ys) a (b ': cs) -> m (DebuggerM m (a ': z ': ys) b cs)+step (Break f _ _ a) = f a++debugM :: Monad m => CascadeM m (a ': b ': cs) -> DebuggerM m '[] a (b ': cs)+debugM (f :>>> fs) = let d = Begin (go f fs d) in d+  where go :: Monad m+           => Kleisli m a b+           -> CascadeM m (b ': cs)+           -> DebuggerM m zs a (b ': cs)+           -> (a -> m (DebuggerM m (a ': zs) b cs))+        go (Kleisli f) Done         d a = do+          b <- f a+          return $ End d a b+        go (Kleisli f) (f' :>>> fs) d a = do+          b <- f a+          let d' = Break (go f' fs d') d a b+          return d'
+ Cascade/Examples.hs view
@@ -0,0 +1,50 @@+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE GADTs                  #-}+module Cascade.Examples where++import Cascade+import Cascade.Debugger++import Control.Category+import Control.Monad+import Data.Char (toUpper)+import Prelude hiding (id, (.))+import System.Environment (getEnv, setEnv, getProgName)++-- example funcitonal cascades+fc, gc :: Cascade '[String, Int, Double, Double]+fc =  read          :>>>+      fromIntegral  :>>>+      (1/)          :>>> Done+gc =  length        :>>>+      (2^)          :>>>+      negate        :>>> Done++-- some example monadic cascades+mc, nc :: CascadeM IO '[ String, (), String, String, () ]+mc =  putStr                  >=>:+      const getLine           >=>:+      return . map toUpper    >=>:+      putStrLn                >=>: Done+nc =  setEnv "foo"            >=>: +      const (return "foo")    >=>:+      getEnv                  >=>:+      print . length          >=>: Done++-- some example comonadic cascades+wc, vc :: CascadeW ((,) Char) '[ Int, Char, Int, String ]+wc =  fst                       =>=:+      fromEnum . snd            =>=:+      uncurry (flip replicate)  =>=: Done+vc =  toEnum . snd              =>=:+      const 5                   =>=:+      show                      =>=: Done++-- run the debugger for the mc cascade all the way to the end+rundmc :: IO (DebuggerM IO '[String, String, (), [Char]] () '[])+rundmc = debugM >>> use "walk this way\n> " >=> step >=> step >=> step $ mc++-- alternate using functions from one cascade then the other+zigzag :: CascadeC c ts -> CascadeC c ts -> CascadeC c ts+zigzag Done Done = Done+zigzag (f :>>> fc) (_ :>>> gc) = f :>>> zigzag gc fc
+ Cascade/Operators.hs view
@@ -0,0 +1,19 @@+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE TypeOperators          #-}+module Cascade.Operators where+import Control.Category (Category(..))+import Control.Comonad (Comonad(..))+import Cascade.Util.ListKind+import Cascade++(#) :: Category c => CascadeC c (t ': ts) -> c t (Last (t ': ts))+(#) = cascade+infixr 0 #++(#~)  :: Monad m => CascadeM m (t ': ts) -> t -> m (Last (t ': ts))+(#~) = cascadeM+infixr 0 #~++(~#) :: Comonad w => CascadeW w (t ': ts) -> w t -> Last (t ': ts)+(~#) = cascadeW+infixr 0 ~#
+ Cascade/Product.hs view
@@ -0,0 +1,113 @@+{-# LANGUAGE KindSignatures         #-}+{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE TypeFamilies           #-}+{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE FlexibleContexts       #-}+{-# LANGUAGE GADTs                  #-}+{-# LANGUAGE RankNTypes             #-}+{-# LANGUAGE UndecidableInstances   #-} -- for RInits+module Cascade.Product where+import Cascade+import Cascade.Util.ListKind++import Control.Arrow (Kleisli(..))+import Control.Comonad (Cokleisli(..), Comonad(..), liftW, (=>>))+import Control.Monad (liftM)+import Control.Monad.Identity (Identity(..))++-- Monadic product+-- +-- Mostly equivalent to +--+--    type family ProductM w ts where+--      ProductM m (a ': as) = (a, m (ProductM m as))+--      ProductM m '[]       = ()+--+-- Made concrete to avoid injective type errors+data ProductM (m :: * -> *) (ts :: [*]) where+  None :: ProductM m '[]+  (:*) :: a -> m (ProductM m ts) -> ProductM m (a ': ts)+infixr 5 :*++-- specialize for the Identity monad, since that'll be common+type Product = ProductM Identity+(*:) :: a -> Product ts -> Product (a ': ts)+a *: as = a :* return as+infixr 5 *:++instance Show (ProductM Identity '[]) where+  showsPrec _ None = showString "None"++instance (Show a, Show (ProductM Identity as)) => Show (ProductM Identity (a ': as)) where+  showsPrec p (a :* (Identity as)) = showParen (p > 10) $ showsPrec 5 a . showString " *: " . showsPrec 5 as++-- Now, ideally, I'd like to be able to lift a `CascadeC c ts` into a chain of+-- transformations between longer and longer `ProductM`s, so when you ultimately+-- ran the cascade, you could generate as much of the intermediate results as+-- you wanted.+--+-- I think this would require something like+--+--    replayC0 :: CascadeWM w m '[a] -> CascadeW w '[ ProductM m '[a] ]+--    replayC0 Done = Done+--    +--    replayC1 :: CascadeWM w m '[a,b] -> CascadeW w '[ ProductM m '[a], ProductM m '[a,b] ]+--    replayC1 (f :>>> Done) = unshifts f :>>> Done+--    +--    replayC2 :: CascadeWM w m '[a,b] +--             -> CascadeW w '[ ProductM m '[a], ProductM m '[a,b], ProductM m '[a, b, c] ]+--    replayC2 (f :>>> g :>>> Done) = unshifts f :>>> unshifts g :>>> Done+--+--    ...+--+--    replayC :: CascadeWM w m ts -> CascadeW w (Map (ProductM m) (Tail (Inits ts)))+--    replayC Done = Done+--    replayC (f :>>> fs) = unshifts f :>>> replayC fs+--+-- where `unshifts` has the given arrow simply append its result to the end of+-- the product+--+--    unshifts :: (w (Last as) -> m b) -> w (ProductM m as) -> ProductM m (Snoc b as)+--+-- But I haven't gotten this to quite work yet.+--+-- For one thing, you actually want `replayC` to be polymorphic in the `init` of+-- the type list. You don't care how long the ProductM is you get - you're just+-- skipping to the end.+--+-- In any case, I suspect this is likely to be slower than normally desired+-- since you have to step all the way through to the end of the `ProductM` each+-- time. Fortunately, the reversed `ProductM Identity` solution is easier and+-- faster+++-- Given ts = [a,b..z], for any ts' this generates the list of `Product`s+--+--  [ Product (a : ts'), Product (b : a : ts'), ..., Product (z : ... : b : a : ts') ]+--+-- Note that the elements of ts are reversed as they are prepended to ts', so+-- the most recently prepended type may be extracted from the front of the `Product`+--+type family RInitProducts (ts :: [*]) (ts' :: [*]) :: [*] where+  RInitProducts (a ': as) ts' = Product (a ': ts') ': RInitProducts as (a ': ts')+  RInitProducts '[] ts' = '[]++-- lift a cokleisli arrow into an arrow on the first element of a product+-- (similar to \f as@(a,_) -> (f a, as) )+pushes :: Comonad w+       => (w y -> x) +       -> w (Product (y ': zs)) -> Product (x ': y ': zs)+pushes f wyzs = f wy *: yzs+  where wy = liftW (\(y :* _) -> y) wyzs+        yzs = extract wyzs++-- transform a comonadic cascade to cache the partial results seen along the way+recordW :: Comonad w+        => CascadeW w (t ': ts) -> CascadeW w (RInitProducts (t ': ts) ts')+recordW Done = Done+recordW (Cokleisli f :>>> fs) = pushes f =>=: recordW fs++-- specialize for functional cascades+record :: Cascade (t ': ts) -> Cascade (RInitProducts (t ': ts) ts')+record = unwrapW . recordW . wrapW
+ Cascade/Sum.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE KindSignatures         #-}+{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE TypeFamilies           #-}+{-# LANGUAGE FlexibleInstances      #-}+{-# LANGUAGE FlexibleContexts       #-}+{-# LANGUAGE GADTs                  #-}+{-# LANGUAGE RankNTypes             #-}+{-# LANGUAGE StandaloneDeriving     #-}+module Cascade.Sum where+import Cascade+import Cascade.Util.ListKind++import Control.Arrow (Kleisli(..))+import Control.Comonad (Cokleisli(..), Comonad(..), liftW, (=>>))+import Control.Monad (liftM)+import Control.Monad.Identity (Identity(..))+import Data.Void++-- Comonadic sum+--+-- Mostly equivalent to +--+--    type family SumW w ts where+--      SumW w (a ': as) = Either (w a) (SumW w as)+--      SumW w '[]       = Void+--+-- Made concrete to avoid injective type errors+data SumW (w :: * -> *) (ts :: [*]) where+  Here  :: w a -> SumW w (a ': as)+  There :: SumW w as -> SumW w (a ': as)+++type family SumW' w (ts :: [*]) where+  SumW' w ('[]) = Void+  SumW' w (a ': as) = Either (w a) (SumW' w as)++toEither :: SumW w as -> SumW' w as+toEither (Here wa)  = Left wa+toEither (There oo) = Right (toEither oo)++-- specialize for the identity comonad, since that'll be common+type Sum = SumW Identity++here :: a -> Sum (a ': as)+here = Here . Identity++there :: Sum as -> Sum (a ': as)+there = There++instance Show (SumW Identity '[]) where+  showsPrec _ _ = error "impossible value of type Sum '[]"++-- show as "here x", "there.here $ x", "there.there.there.here $ x" to avoid lisping+instance (Show a, Show (SumW Identity as)) => Show (SumW Identity (a ': as)) where+  showsPrec (-1) (Here (Identity a))  = showString "here $ " . showsPrec 0 a+  showsPrec (-1) (There as)           = showString "there." . showsPrec (-1) as+  showsPrec p (Here (Identity a))     = showParen (p > 10) $ showString "here " . showsPrec 11 a+  showsPrec p (There as)              = showParen True $ showString "there." . showsPrec (-1) as++-- This could be more simply expressed as+--+--     type TailSumsW w ts = Map (SumW w) (Init (Tails ts))+--+-- however, GHC can't quite figure out the equivalences we need+--+--     Could not deduce (Map (SumW w) (Init ((y : zs) : Tails zs)) ~ (SumW w (y : zs) : zs0))+--+-- XXX: This type family is actually more restrictive than we need - we should+-- actually use  `SumW w (a ': Concat as bs)`, as we can pass through any b+-- values untouched. Haven't gotten that to work yet though.+type family TailSumsW (w :: * -> *) (ts :: [*]) :: [*] where+  TailSumsW w '[] = '[]+  TailSumsW w (a ': as) = SumW w (a ': as) ': TailSumsW w as+type TailSums ts = TailSumsW Identity ts++-- lift a kleisli arrow into an arrow on the first element of a sum (if given)+-- (similar to \f -> either f id)+pops :: Monad m+     => (w x -> m (w y))+     -> SumW w (x ': y ': zs) -> m (SumW w (y ': zs))+pops _ (There oo) = return oo+pops f (Here wx)  = liftM Here $ f wx++-- transform a categorical cascade into a monadic cascade, resumable from any+-- point in the computation (by passing the proper sum value as input)+resumeC :: Monad m+        => (forall a b. c a b -> w a -> m (w b))+        -> CascadeC c ts +        -> CascadeM m (TailSumsW w ts)+resumeC over Done = Done+resumeC over (f :>>> fs) = pops (over f) >=>: resumeC over fs++-- specialize to monadic cascades+resumeM :: Monad m => CascadeM m ts -> CascadeM m (TailSums ts)+resumeM = resumeC $ \c -> liftM Identity . runKleisli c . runIdentity++-- specialize to comonadic cascades+resumeW :: Comonad w => CascadeW w ts -> Cascade (TailSumsW w ts)+resumeW = unwrapM . resumeC (\c -> Identity . (=>> runCokleisli c))++-- specialize to functional cascades+resume :: Cascade ts -> Cascade (TailSums ts)+resume  = unwrapM . resumeC (\c -> fmap (Identity . c))++-- unwrap the output for the user+-- resume' :: Cascade ts -> Cascade (Snoc (TailSums ts (Last ts))+-- resume' fs = resume fs 
+ Cascade/Util/ListKind.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE KindSignatures         #-}+{-# LANGUAGE DataKinds              #-}+{-# LANGUAGE TypeOperators          #-}+{-# LANGUAGE PolyKinds              #-}+{-# LANGUAGE TypeFamilies           #-}+{-# LANGUAGE UndecidableInstances   #-} -- for RInits+module Cascade.Util.ListKind where+import GHC.Prim (Constraint)++-- type level version of all :: (a -> Bool) -> [a] -> Bool+type family All (c :: a -> Constraint) (xs :: [a]) :: Constraint where+  All c '[] = ()+  All c (a ': as) = (c a, All c as)++-- type level version of last :: [a] -> a+type family Last (ts :: [a]) :: a where+  Last '[a] = a+  Last (a ': as) = Last as++-- type level version of map :: (a -> b) -> [a] -> [b]+type family Map (f :: a -> b) (ts :: [a]) :: [b] where+  Map f '[] = '[]+  Map f (a ': as) = f a ': Map f as++-- type level version of tails :: [a] -> [[a]]+type family Tails (as :: [a]) :: [[a]] where+  Tails '[] = '[ '[] ]+  Tails (a ': as) = (a ': as) ': Tails as++-- type level version of tail :: [a] -> [a]+type family Tail (as :: [a]) :: [a] where+  Tail (a ': as) = as++-- type level version of init :: [a] -> [a]+type family Init (as :: [a]) :: [a] where+  Init '[a] = '[]+  Init (a ': as)  = a ': Init as++-- type level version of rinits :: [a] -> [[a]] ; rinits = reverse . inits . reverse+type family RInits (as :: [a]) :: [[a]] where+  RInits '[] = '[ '[] ]+  RInits (a ': as) = '[] ': Map (Snoc a) (RInits as)++-- type level version of snoc :: a -> [a] -> [a] ; snoc a = reverse . (a:) . reverse+type family Snoc (x :: a) (xs :: [a]) :: [a] where+  Snoc x '[] = '[x]+  Snoc x (a ': as) = a ': Snoc x as++-- type level version of zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]+type family ZipWith (f :: a -> b -> c) (as :: [a]) (bs :: [b]) :: [c] where+  ZipWith f '[] '[] = '[]+  ZipWith f (a ': as) (b ': bs) = f a b ': ZipWith f as bs++type family Concat (as :: [a]) (bs :: [a]) :: [a] where+  Concat '[] bs = bs+  Concat (a ': as) bs = a ': Concat as bs
+ LICENSE view
@@ -0,0 +1,1 @@+public domain
+ README.md view
@@ -0,0 +1,212 @@+<b>`Cascade`</b>s are collections of composable functions (e.g. `a -> b, b -> c, ... , y -> z`) where the intermediate types are stored in a type level list (e.g. `Cascade [a,b,c,...,y,z]`).++For example, consider these `Cascade`s:++```haskell+fc :: Cascade '[String, Int, Double, Double]+fc =  read          :>>>+      fromIntegral  :>>>+      (1/)          :>>> Done++gc :: Cascade '[String, Int, Double, Double]+gc =  length        :>>>+      (2^)          :>>>+      negate        :>>> Done+```++We can convert a cascade into a function easily enough:++```haskell+λ :m +Cascade.Examples Cascade.Operators+λ fc # "5"+0.2+λ gc # "20"+-4.0+```++But that's not very inspiring. The real question, [as Christian Conkle put it](http://stackoverflow.com/questions/26593237/what-would-the-type-of-a-list-of-cascading-functions-be#comment41802812_26593534), is "what does such a collection give you over function composition?"++Because none of the type information has been lost, we can still extract each of the functions that went into the `Cascade` using simple pattern matching. This opens the door to replacing parts of a cascade, or indexing into the cascade with type-level naturals.++It also lets us do something silly like mix and match two different cascades:++```haskell+λ zigzag fc gc # "3" -- read >>> (2^) >>> (1/)+0.125+λ zigzag gc fc # "123456789" -- length >>> fromIntegral >>> negate+-9.0+```++More seriously, we can record the intermediate results of each `Cascade` using a `Product` type as output:++```haskell+λ :m +Cascade.Product+λ record fc # "5" *: None+0.2 *: 5.0 *: 5 *: "5" *: None+λ record gc # "5" *: None+-2.0 *: 2.0 *: 1 *: "5" *: None+```++Or I can resume the computation at some later point rather that the first function in the `Cascade` using a `Sum` type as input:++```haskell+λ :m +Cascade.Sum+λ resume fc # (there.there.here) 0.2+here 5.0+λ resume gc # (there.here) 0+here (-1.0)+```++Or we could do both:++```haskell+λ resume (record fc) # (there.here) (17 *: "foo" *: None)+here (5.8823529411764705e-2 *: 17.0 *: 17 *: "foo" *: None)+λ record (resume fc) # (there . there $ here 0.25) *: None+here 4.0 *: here 0.25 *: (there.here $ 0.25) *: (there.there.here $ 0.25) *: None+```++But what's nice is that this generalizes nicely to categorical composition, so we can do the same with +any category, including the `Kleisli` and `Cokleisli` categories for `Monad`s and `Comonad`s, respectively:++```haskell+-- some example monadic cascades+mc, nc :: CascadeM IO '[ String, (), String, String, () ]+mc =  putStr                  >=>:+      const getLine           >=>:+      return . map toUpper    >=>:+      putStrLn                >=>: Done+nc =  setEnv "foo"            >=>: +      const (return "foo")    >=>:+      getEnv                  >=>:+      print . length          >=>: Done+```++```haskell+-- some example comonadic cascades+wc, vc :: CascadeW ((,) Char) '[ Int, Char, Int, String ]+wc =  fst                       =>=:+      fromEnum . snd            =>=:+      uncurry (flip replicate)  =>=: Done+vc =  toEnum . snd              =>=:+      const 5                   =>=:+      show                      =>=: Done+```++Flipping back to ghci:++```haskell+λ mc #~ "? "+? i like cheese+I LIKE CHEESE+λ nc #~ "? "+2+λ wc ~# ('.', 5)+".............................................."+λ vc ~# ('x', 9)+"('x',5)"+λ zigzag mc nc #~ "hi!"+hi!3+λ zigzag nc mc #~ "hello."+USER+rampion+λ zigzag wc vc ~# ('.', 3)+"....."+λ zigzag vc wc ~# ('a', 9)+"('a',9)"+```++`resume` works on both comonads and monads:++```haskell+λ resumeM nc #~ (there.there.here) "USER"+7+here ()+λ toEither $ resumeW vc # (There . There . Here) ('c',9)+Left ('c',"('c',9)")+```++`record` works on comonads, but I've been having some issues getting it to work the way I want on monads (see `Cascade.Product.hs` for more).++So, instead of continue to wrestle the type system, for now, I just implemented a debugger that uses `Cascade`s,+so in addition running a monadic `Cascade` you can debug it.++```haskell+-- run the debugger for the mc cascade all the way to the end+rundmc :: IO (DebuggerM IO '[String, String, (), [Char]] () '[])+rundmc = debugM >>> use "walk this way\n> " >=> step >=> step >=> step $ mc+```++Dropping into ghci:++```haskell+λ d <- rundmc+walk this way+> talk this way+TALK THIS WAY+```++We can see the current state of the debugger:++```haskell+λ d+End   { given = "TALK THIS WAY", returned = () }+```++the full stack trace++```haskell+λ printHistory d+End   { given = "TALK THIS WAY", returned = () }+Break { given = "talk this way", returned = "TALK THIS WAY" }+Break { given = (), returned = "talk this way" }+Break { given = "walk this way\n> ", returned = () }+Begin+```++back up, step forward:++```haskell+λ back d+Break { given = "talk this way", returned = "TALK THIS WAY" }+λ back it+Break { given = (), returned = "talk this way" }+λ step it+Break { given = "talk this way", returned = "TALK THIS WAY" }+λ step it+TALK THIS WAY+End   { given = "TALK THIS WAY", returned = () }+```++(Note that when we step forward, the monadic computation reruns)++We can also use a different input than the default at the current stage:++```haskell+λ back d+Break { given = "talk this way", returned = "TALK THIS WAY" }+λ d' <- use "(talk this way)" it+(talk this way)+λ printHistory d'+End   { given = "(talk this way)", returned = () }+Break { given = "talk this way", returned = "TALK THIS WAY" }+Break { given = (), returned = "talk this way" }+Break { given = "walk this way\n> ", returned = () }+Begin+```++And since the debuggers are normal immutable haskell values, we can use both `d` and `d'` without errors.++---++`Cascade`s are still very limited. They're linear, and of a set length. They don't let you hook into functions that call themselves recursively, or functions that have computation paths better represented by trees or lattices.++But that doesn't mean those are necessarily impossible to model, either. For example, simple recursion is fairly easily captured with only a slight modification to `Cascade`++```haskell+data CascadeR (ts :: [*]) where+  (:>>>)  :: x -> y -> CascadeR (y ': zs) -> CascadeR (x ': y ': zs)+  Fix     :: ((x -> y) -> x -> y) -> CascadeR (y ': zs) -> CascadeR (x ': y ': zs)+  Done    :: CascadeR '[t]+```+
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain