{-# LANGUAGE TypeOperators #-} {-# OPTIONS -Wall #-} ---------------------------------------------------------------------- -- | -- Module : Control.Arrow.DeepArrow -- Copyright : (c) Conal Elliott 2006 -- License : BSD3 -- -- Maintainer : conal@conal.net -- Stability : experimental -- Portability : portable -- -- \"Deep arrows\" as an 'Arrow' subclass. ---------------------------------------------------------------------- module Control.Arrow.DeepArrow ( -- * The DeepArrow class DeepArrow(..) -- * Composable function extractors , funFirst, funSecond, funResult -- * Composable input extractors , inpF, inpS, inpFirst, inpSecond -- * Misc functions , flipA, unzipA -- * 'DeepArrow' instance helper , FunDble(..) -- * Some utilities , (->|) ) where import Prelude hiding (id,(.)) import Control.Category import Control.Arrow import Control.Compose ((::*::)(..),(*::*),inProdd,FunA(..),inFunA, FunAble) import Data.FunArr {---------------------------------------------------------- The "deep arrow" class ----------------------------------------------------------} {- | Arrows for deep application. Most of these methods could be defined using 'arr', but 'arr' is not definable for some types. If your 'DeepArrow' instance has 'arr', you might want to use these implementations @ 'fstA' = 'arr' 'fst' 'dupA' = 'arr' (\\ x -> (x,x)) 'sndA' = 'arr' 'snd' 'funF' = 'arr' (\\ (f,b) -> \\ c -> (f c, b)) 'funS' = 'arr' (\\ (a,f) -> \\ c -> (a, f c)) 'funR' = 'arr' 'flip' 'curryA' = 'arr' 'curry' 'uncurryA' = 'arr' 'uncurry' 'swapA' = 'arr' (\\ (a,b) -> (b,a)) 'lAssocA' = 'arr' (\\ (a,(b,c)) -> ((a,b),c)) 'rAssocA' = 'arr' (\\ ((a,b),c) -> (a,(b,c))) @ If your 'DeepArrow' instance /does not/ have 'arr', you'll have to come up with other definitions. In any case, I recommend the following definitions, which mirror 'Arrow' defaults while avoiding 'arr'. Be sure also to define 'arr' or 'pure' to yield an error message (rather than ping-ponging infinitely between them via the 'Arrow' default definitions). @ 'second' f = 'swapA' '.' 'first' f '.' 'swapA' f '&&&' g = 'dupA' '>>>' f '***' g @ In a few cases, there are default methods, as noted below. The defaults do not use 'arr'. -} class Arrow (~>) => DeepArrow (~>) where -- | Direct arrow into a function's result. Analogous to 'first' and -- 'second'. result :: (b ~> b') -> ((a->b) ~> (a->b')) -- Complicates OFun considerably and not used. -- Direct arrow into a function's argument. Note contravariance. -- argument :: (a' ~> a ) -> ((a->b) ~> (a'->b)) -- -- | Identity. Unnecessary now that we have Category -- idA :: a ~> a -- idA = id -- | Duplicate. dupA :: a ~> (a,a) -- | Extract first. fstA :: (a,b) ~> a -- | Extract second. sndA :: (a,b) ~> b -- | Extract function from first element. funF :: (c->a, b) ~> (c->(a,b)) -- | Extract function from second element. funS :: (a, c->b) ~> (c->(a,b)) -- Could default via swapA & funF -- | Extract function from result. funR :: (a->c->b) ~> (c->a->b) -- | Curry arrow. curryA :: ((a,b)->c) ~> (a->b->c) -- | Uncurry arrow. uncurryA :: (a->b->c) ~> ((a,b)->c) -- | Swap elements. Has default. swapA :: (a,b) ~> (b,a) swapA = sndA &&& fstA -- | Left-associate. Has default. lAssocA :: (a,(b,c)) ~> ((a,b),c) lAssocA = second fstA &&& (sndA . sndA) -- | Right-associate. Has default. rAssocA :: ((a,b),c) ~> (a,(b,c)) rAssocA = (fstA . fstA) &&& first sndA -- I don't think this one is used. -- composeA :: Arrow (~~>) => (a ~~> b, b ~~> c) ~> (a ~~>c) -- composeA = arr (uncurry (>>>)) {---------------------------------------------------------- Composable function extractors ----------------------------------------------------------} -- | Promote a function extractor into one that reaches into the first -- element of a pair. funFirst :: DeepArrow (~>) => (a ~> (d->a')) -> ((a,b) ~> (d->(a',b))) -- | Promote a function extractor into one that reaches into the second -- element of a pair. funSecond :: DeepArrow (~>) => (b ~> (d->b')) -> ((a,b) ~> (d->(a,b'))) -- | Promote a function extractor into one that reaches into the result -- element of a function. funResult :: DeepArrow (~>) => (b ~> (d->b')) -> ((a->b) ~> (d->(a->b'))) funFirst h = funF . first h funSecond h = funS . second h funResult h = funR . result h {---------------------------------------------------------- Composable input extractors ----------------------------------------------------------} -- | Extract the first component of a pair input. inpF :: DeepArrow (~>) => ((a,b) -> c) ~> (a -> (b->c)) inpF = curryA -- | Extract the second component of a pair input. inpS :: DeepArrow (~>) => ((a,b) -> c) ~> (b -> (a->c)) inpS = flipA . curryA -- | Given a way to extract a @d@ input from an @a@ input, leaving an @a'@ -- residual input, 'inpFirst' yields a way to extract a @d@ input from an -- @(a,b)@ input, leaving an @(a',b)@ residual input. inpFirst :: DeepArrow (~>) => (( a ->c) ~> (d -> ( a' ->c))) -> (((a,b)->c) ~> (d -> ((a',b)->c))) -- | Analogous to 'inpFirst'. inpSecond :: DeepArrow (~>) => (( b ->c) ~> (d -> ( b' ->c))) -> (((a,b)->c) ~> (d -> ((a,b')->c))) -- See ICFP 07 paper for the derivation of inpFirst and inpSecond inpFirst h = result (uncurryA . flipA) . flipA . result h . flipA . curryA inpSecond h = result uncurryA . flipA . result h . curryA {---------------------------------------------------------- Misc functions ----------------------------------------------------------} -- | Flip argument order flipA :: DeepArrow (~>) => (a->c->b) ~> (c->a->b) flipA = funR -- | Like 'unzip' but for 'DeepArrow' arrows instead of lists. unzipA :: DeepArrow (~>) => (a ~> (b,c)) -> (a ~> b, a ~> c) unzipA f = (fstA . f, sndA . f) {---------------------------------------------------------- Some 'DeepArrow' instances ----------------------------------------------------------} instance DeepArrow (->) where result = (.) -- argument = flip (.) -- Since (->) implements 'arr', use the recommended defaults for the rest. -- idA = arr id fstA = arr fst dupA = arr (\x->(x,x)) sndA = arr snd funF = arr (\ (f,b) -> \ c -> (f c, b)) funS = arr (\ (a,f) -> \ c -> (a, f c)) funR = arr flip curryA = arr curry uncurryA = arr uncurry swapA = arr (\ (a,b) -> (b,a)) lAssocA = arr (\ (a,(b,c)) -> ((a,b),c)) rAssocA = arr (\ ((a,b),c) -> (a,(b,c))) -- DeepArrow "pairs" are deep arrows instance (DeepArrow ar, DeepArrow ar') => DeepArrow (ar ::*:: ar') where dupA = dupA *::* dupA fstA = fstA *::* fstA sndA = sndA *::* sndA funF = funF *::* funF funS = funS *::* funS funR = funR *::* funR curryA = curryA *::* curryA uncurryA = uncurryA *::* uncurryA swapA = swapA *::* swapA lAssocA = lAssocA *::* lAssocA rAssocA = rAssocA *::* rAssocA result = inProdd (result *** result) -- idA = idA *::* idA -- composeA = Prodd (composeA, composeA) -- argument (Prodd (f,f')) = Prodd (argument f, argument f') -- | Support needed for a 'FunA' to be a 'DeepArrow' (as 'FunAble' serves -- 'Arrow'). class FunAble h => FunDble h where resultFun :: (h b -> h b') -> (h (a->b) -> h (a->b')) dupAFun :: h a -> h (a,a) fstAFun :: h (a,b) -> h a sndAFun :: h (a,b) -> h b funFFun :: h (c->a, b) -> h (c->(a,b)) funSFun :: h (a, c->b) -> h (c->(a,b)) funRFun :: h (a->c->b) -> h (c->a->b) curryAFun :: h ((a,b)->c) -> h (a->b->c) uncurryAFun :: h (a->b->c) -> h ((a,b)->c) swapAFun :: h (a,b) -> h (b,a) lAssocAFun :: h (a,(b,c)) -> h ((a,b),c) rAssocAFun :: h ((a,b),c) -> h (a,(b,c)) instance FunDble h => DeepArrow (FunA h) where result = inFunA resultFun -- idA = FunA id dupA = FunA dupAFun fstA = FunA fstAFun sndA = FunA sndAFun funF = FunA funFFun funS = FunA funSFun funR = FunA funRFun curryA = FunA curryAFun uncurryA = FunA uncurryAFun swapA = FunA swapAFun lAssocA = FunA lAssocAFun rAssocA = FunA rAssocAFun -- (>>>) = inFunA2 (>>>) -- first = inFunA firstFun -- second = inFunA secondFun -- (***) = inFunA2 (***%) -- (&&&) = inFunA2 (&&&%) {---------------------------------------------------------- Some utilities ----------------------------------------------------------} -- | Compose wrapped functions (->|) :: (DeepArrow (~>), FunArr (~>) w) => w (a->b) -> w (b->c) -> w (a->c) (->|) f g = result (toArr g) $$ f -- -- | Pre- and post-processing -- (~>) :: DeepArrow (-->) => (a' --> a) -> (b --> b') -> ((a -> b) --> (a' -> b')) -- f ~> h = result h . argument f