{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} -- ------------------------------------------------------------ {- | Module : Control.Arrow.IOStateListArrow Copyright : Copyright (C) 2005-8 Uwe Schmidt License : MIT Maintainer : Uwe Schmidt (uwe\@fh-wedel.de) Stability : experimental Portability: portable Implementation of arrows with IO and a state -} -- ------------------------------------------------------------ module Control.Arrow.IOStateListArrow ( IOSLA(..) , liftSt , runSt ) where import Prelude hiding (id, (.)) import Control.Category import Control.Arrow import Control.Arrow.ArrowExc import Control.Arrow.ArrowIf import Control.Arrow.ArrowIO import Control.Arrow.ArrowList import Control.Arrow.ArrowNF import Control.Arrow.ArrowTree import Control.Arrow.ArrowNavigatableTree import Control.Arrow.ArrowState import Control.DeepSeq import Control.Exception ( SomeException , try ) {- import qualified Debug.Trace as T -} -- ------------------------------------------------------------ -- | list arrow combined with a state and the IO monad newtype IOSLA s a b = IOSLA { runIOSLA :: s -> a -> IO (s, [b]) } instance Category (IOSLA s) where id = IOSLA $ \ s x -> return (s, [x]) -- don't defined id = arr id, this gives loops during optimization {-# INLINE id #-} IOSLA g . IOSLA f = IOSLA $ \ s x -> do (s1, ys) <- f s x sequence' s1 ys where sequence' s' [] = return (s', []) sequence' s' (x':xs') = do (s1', ys') <- g s' x' (s2', zs') <- sequence' s1' xs' return (s2', ys' ++ zs') instance Arrow (IOSLA s) where arr f = IOSLA $ \ s x -> return (s, [f x]) {-# INLINE arr #-} first (IOSLA f) = IOSLA $ \ s (x1, x2) -> do (s', ys1) <- f s x1 return (s', [ (y1, x2) | y1 <- ys1 ]) -- just for efficiency second (IOSLA g) = IOSLA $ \ s (x1, x2) -> do (s', ys2) <- g s x2 return (s', [ (x1, y2) | y2 <- ys2 ]) -- just for efficiency IOSLA f *** IOSLA g = IOSLA $ \ s (x1, x2) -> do (s1, ys1) <- f s x1 (s2, ys2) <- g s1 x2 return (s2, [ (y1, y2) | y1 <- ys1, y2 <- ys2 ]) -- just for efficiency IOSLA f &&& IOSLA g = IOSLA $ \ s x -> do (s1, ys1) <- f s x (s2, ys2) <- g s1 x return (s2, [ (y1, y2) | y1 <- ys1, y2 <- ys2 ]) instance ArrowZero (IOSLA s) where zeroArrow = IOSLA $ \ s -> const (return (s, [])) {-# INLINE zeroArrow #-} instance ArrowPlus (IOSLA s) where IOSLA f <+> IOSLA g = IOSLA $ \ s x -> do (s1, rs1) <- f s x (s2, rs2) <- g s1 x return (s2, rs1 ++ rs2) instance ArrowChoice (IOSLA s) where left (IOSLA f) = IOSLA $ \ s -> either (\ x -> do (s1, y) <- f s x return (s1, map Left y) ) (\ x -> return (s, [Right x])) right (IOSLA f) = IOSLA $ \ s -> either (\ x -> return (s, [Left x])) (\ x -> do (s1, y) <- f s x return (s1, map Right y) ) instance ArrowApply (IOSLA s) where app = IOSLA $ \ s (IOSLA f, x) -> f s x {-# INLINE app #-} instance ArrowList (IOSLA s) where arrL f = IOSLA $ \ s x -> return (s, (f x)) {-# INLINE arrL #-} arr2A f = IOSLA $ \ s (x, y) -> runIOSLA (f x) s y {-# INLINE arr2A #-} constA c = IOSLA $ \ s -> const (return (s, [c])) {-# INLINE constA #-} isA p = IOSLA $ \ s x -> return (s, if p x then [x] else []) {-# INLINE isA #-} IOSLA f >>. g = IOSLA $ \ s x -> do (s1, ys) <- f s x return (s1, g ys) {-# INLINE (>>.) #-} -- just for efficency perform (IOSLA f) = IOSLA $ \ s x -> do (s1, _ys) <- f s x return (s1, [x]) {-# INLINE perform #-} instance ArrowIf (IOSLA s) where ifA (IOSLA p) ta ea = IOSLA $ \ s x -> do (s1, res) <- p s x runIOSLA ( if null res then ea else ta ) s1 x (IOSLA f) `orElse` g = IOSLA $ \ s x -> do r@(s1, res) <- f s x if null res then runIOSLA g s1 x else return r instance ArrowIO (IOSLA s) where arrIO cmd = IOSLA $ \ s x -> do res <- cmd x return (s, [res]) {-# INLINE arrIO #-} instance ArrowExc (IOSLA s) where tryA f = IOSLA $ \ s x -> do res <- try' $ runIOSLA f s x return $ case res of Left er -> (s, [Left er]) Right (s1, ys) -> (s1, [Right x' | x' <- ys]) where try' :: IO a -> IO (Either SomeException a) try' = try instance ArrowIOIf (IOSLA s) where isIOA p = IOSLA $ \ s x -> do res <- p x return (s, if res then [x] else []) {-# INLINE isIOA #-} instance ArrowState s (IOSLA s) where changeState cf = IOSLA $ \ s x -> let s' = cf s x in return (seq s' s', [x]) {-# INLINE changeState #-} accessState af = IOSLA $ \ s x -> return (s, [af s x]) {-# INLINE accessState #-} -- ------------------------------------------------------------ -- | -- lift the state of an IOSLA arrow to a state with an additional component. -- -- This is uesful, when running predefined IO arrows, e.g. for document input, -- in a context with a more complex state component. liftSt :: IOSLA s1 b c -> IOSLA (s1, s2) b c liftSt (IOSLA f) = IOSLA $ \ (s1, s2) x -> do (s1', ys) <- f s1 x return ((s1', s2), ys) -- | -- run an arrow with augmented state in the context of a simple state arrow. -- An initial value for the new state component is needed. -- -- This is useful, when running an arrow with an extra environment component, e.g. -- for namespace handling in XML. runSt :: s2 -> IOSLA (s1, s2) b c -> IOSLA s1 b c runSt s2 (IOSLA f) = IOSLA $ \ s1 x -> do ((s1', _s2'), ys) <- f (s1, s2) x return (s1', ys) -- ------------------------------------------------------------ instance ArrowTree (IOSLA s) instance ArrowNavigatableTree (IOSLA s) instance ArrowNF (IOSLA s) where rnfA (IOSLA f) = IOSLA $ \ s x -> do res <- f s x ( -- T.trace "start rnfA for IOSLA" $ snd res ) `deepseq` return ( -- T.trace "end rnfA for IOSLA" $ res ) instance ArrowWNF (IOSLA s) -- ------------------------------------------------------------