> {-# LANGUAGE CPP, TemplateHaskell, FlexibleInstances #-}
> module Control.CCA.CCNF 
>   (norm, normOpt, 
>    pprNorm, pprNormOpt, printCCA, ASyn,
>    cross, dup, swap, assoc, unassoc, juggle, trace, mirror, untag, tagT, untagT) where
#if __GLASGOW_HASKELL__ >= 610
> import Control.Category
> import Prelude hiding ((.), id, init)
#else
> import Prelude hiding (init)
#endif
> import Control.Arrow 
> import Control.CCA.Types
> import Data.Char (isAlpha)
> import Language.Haskell.TH
> import Language.Haskell.TH.Syntax
> import Language.Haskell.TH.Instances
> import qualified Data.Generics as G (everywhere, mkT)
Internal Representation ======================= We use AExp to syntactically represent an arrow for normalization purposes.
> data AExp
>   = Arr ExpQ
>   | First AExp
>   | AExp :>>> AExp
>   | Loop AExp
>   | LoopD ExpQ ExpQ -- loop with initialized feedback
>   | Init ExpQ
>   | Lft AExp
> infixl 1 :>>>
We use phantom types to make ASyn an Arrow.
> newtype ASyn b c = AExp AExp 
#if __GLASGOW_HASKELL__ >= 610
> instance Category ASyn where
>   id = AExp (Arr [|\x -> x|])
>   AExp g . AExp f = AExp (f :>>> g)
> instance Arrow ASyn where
>   arr f = error "use arr' instead" 
>   first (AExp f) = AExp (First f)
#else
> instance Arrow ASyn where
>   arr f = error "use arr' instead" 
>   AExp f >>> AExp g = AExp (f :>>> g)
>   first (AExp f) = AExp (First f)
#endif
> instance ArrowLoop ASyn where
>   loop (AExp f) = AExp (Loop f)
> instance ArrowInit ASyn where
>   init i = error "use init' instead"
>   arr' f _ = AExp (Arr f)
>   init' i _ = AExp (Init i)
ArrowChoice only requires definition for 'left', but the default implementation for 'right' and '|||' uses arr so we need to redefine them using arr' here. '+++' is also redefined here for completeness.
> instance ArrowChoice ASyn where
>   left (AExp f) = AExp (Lft f)
>   right f = arr' [| mirror |] mirror >>> left f
>             >>> arr' [| mirror |] mirror
>   f +++ g = left f >>> right g
>   f ||| g = f +++ g >>> arr' [| untag |] untag
Pretty printing AExp.
> printCCA (AExp x) = printAExp x
> printAExp x = runQ (fromAExp x) >>= putStrLn . simplify . pprint
> simplify = unwords . map (unwords . map aux . words) . lines 
>   where aux (c:x) | not (isAlpha c) = c : aux x
>         aux x = let (u, v) = break (=='.') x
>                 in if length v > 1 then aux (tail v)
>                                    else x
Traversal over AExp is defined in terms of imap (intermediate map) and everywhere.
> type Traversal = AExp -> AExp
> imap :: Traversal -> Traversal
> imap h (First f) = First (h f)
> imap h (f :>>> g) = h f :>>> h g
> imap h (Loop f) = Loop (h f)
> imap h (Lft f) = Lft (h f)
> imap h x = x
> everywhere :: Traversal -> Traversal 
> everywhere h = h . imap (everywhere h)
Normalization ============= norm is a TH function that normalizes a given CCA, e.g., $(norm e) will give the CCNF of e.
> norm :: ASyn t t1 -> ExpQ         -- returns a generic ArrowInit arrow
> norm (AExp e) = fromAExp (normE e)
> normE = everywhere normalize 
normOpt returns the pair of state and pure function as (i, f) from optimized CCNF in the form loopD i (arr f).
> normOpt :: ASyn t t1 -> ExpQ      -- returns a pair of state and pure function (s, f)
> normOpt (AExp e) = 
>   case normE e of
>     LoopD i f -> tupE [i, f]
>     Arr f     -> [| ( (), $(f) ) |]
>     _         -> error "The given arrow can't be normalized to optimized CCNF."
pprNorm and pprNormOpt return the pretty printed normal forms as a string.
> pprNorm = ppr' . norm
> pprNormOpt = ppr' . normOpt
> ppr' e = runQ (fmap toLet e) >>= litE . StringL . simplify . pprint
fromAExp converts AExp back to TH Exp structure.
> fromAExp :: AExp -> ExpQ
> fromAExp (Arr f) = appE [|arr|] f
> fromAExp (First f) = appE [|first|] (fromAExp f)
> fromAExp (f :>>> g) = infixE (Just (fromAExp f)) [|(>>>)|] (Just (fromAExp g))
> fromAExp (Loop f) = appE [|loop|] (fromAExp f)
> fromAExp (LoopD i f) = appE (appE [|loopD|] i) f
> fromAExp (Init i) = appE [|init|] i
> fromAExp (Lft f) = appE [|left|] (fromAExp f)
CCNF ==== Arrow laws:
> normalize (Arr f :>>> Arr g) = Arr (g `o` f)
> normalize (First (Arr f)) = Arr (f `crossE` idE)
> normalize (Arr f :>>> LoopD i g) = LoopD i (g `o` (f `crossE` idE))
> normalize (LoopD i f :>>> Arr g) = LoopD i ((g `crossE` idE) `o` f)
> normalize (LoopD i f :>>> LoopD j g) = LoopD (tupE [i,j]) 
>   (assocE `o` juggleE `o` (g `crossE` idE) `o` juggleE `o` (f `crossE` idE) `o` assocE')
> normalize (Loop (LoopD i f)) = LoopD i (traceE (juggleE `o` f `o` juggleE))
> normalize (First (LoopD i f)) = LoopD i (juggleE `o` (f `crossE` idE) `o` juggleE)
> normalize (Init i) = LoopD i swapE
Choice:
> normalize (Lft (Arr f)) = Arr (lftE f)
> normalize (Lft (LoopD i f)) = LoopD i (untagE `o` lftE f `o` tagE)
All the other cases are unchanged.
> normalize e = e 
To Let-Expression ================= Transform function applications to let-expressions. (\x -> e1) e2 === let x = e2 in e1
> toLet :: Exp -> Exp
> toLet = G.everywhere (G.mkT aux)
>   where
>     aux (AppE (LamE [pat] body) arg) = LetE [ValD pat (NormalB arg) []] body
>     aux (AppE (LamE (pat:ps) body) arg) = LamE ps (LetE [ValD pat (NormalB arg) []] body)
>     aux x = x
Auxiliary Functions ===================
> dup x = (x, x)
> swap (x, y) = (y, x)
> unassoc (x, (y, z)) = ((x, y), z)
> assoc ((x, y), z) = (x, (y, z))
> juggle ((x, y), z) = ((x, z), y)
> trace f x = let (y, z) = f (x, z) in y
> cross f g (x, y) = (f x, g y)
> mirror (Left x) = Right x
> mirror (Right y) = Left y
> untag (Left x) = x
> untag (Right y) = y
> lft f x = case x of
>   Left  u -> Left (f u)
>   Right u -> Right u 
> tagT (x, y) = case x of
>   Left  u -> Left  (u, y)
>   Right u -> Right (u, y)
> untagT z = case z of
>   Left  (x, y) -> (Left  x, y)
>   Right (x, y) -> (Right x, y)
> o :: ExpQ -> ExpQ -> ExpQ
> f `o` g = appE (appE [|(.)|] f) g
> f `crossE` g = appE (appE [|cross|] f) g
> idE :: ExpQ
> idE = [|id|]
> dupE = [|dup|]
> swapE = [|swap|]
> assocE = [|assoc|]
> assocE' = [|unassoc|]
> juggleE = [|juggle|]
> traceE = appE [|trace|]
> tagE = [|tagT|]
> untagE = [|untagT|]
> lftE = appE [|lft|]