-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Iteratee-based IO with pipe operators -- -- Iteratee-based IO is an alternative to lazy IO that offers better -- error handling, referential transparency, and convenient composition -- of protocol layers or parsers. This package provides iteratees based -- around pipe operators for hooking together application -- components and directing data flow. New users should see the tutorial -- in the Data.IterIO module documentation. Highlights of the -- library include: -- --
-- import Prelude hiding (null) --class Monoid t => ChunkData t null :: ChunkData t => t -> Bool chunkShow :: ChunkData t => t -> String -- | Chunk is a wrapper around a ChunkData type that also -- includes an EOF flag that is True if the data is followed by an -- end-of-file condition. An Iter that receives a Chunk -- with EOF True must return a result (or failure); it is an error -- to demand more data (return IterF) after an EOF. data Chunk t Chunk :: !t -> !Bool -> Chunk t -- | Constructor function that builds a chunk containing data and a -- False EOF flag. chunk :: t -> Chunk t -- | An chunk with mempty data and the EOF flag True. chunkEOF :: Monoid t => Chunk t -- | The basic Iteratee type is Iter t m a, where t is -- the type of input (in class ChunkData), m is a monad -- in which the iteratee may execute actions (using the MonadTrans -- lift method), and a is the result type of the -- iteratee. -- -- Internally, an Iter is a function from an input Chunk -- to a result of type IterR. newtype Iter t m a Iter :: (Chunk t -> IterR t m a) -> Iter t m a runIter :: Iter t m a -> Chunk t -> IterR t m a -- | Class of control commands for enclosing enumerators. The class binds -- each control argument type to a unique result type. class (Typeable carg, Typeable cres) => CtlCmd carg cres | carg -> cres -- | The outcome of an IterC request. data CtlRes a -- | The request type was not supported by the enumerator. CtlUnsupp :: CtlRes a -- | The request was supported, and executing it caused an exception to be -- thrown. CtlFail :: !SomeException -> CtlRes a -- | The result of the control operation. CtlDone :: !a -> CtlRes a -- | Used when an Iter is issuing a control request to an enclosing -- enumerator. Note that unlike IterF or IterM, control -- requests expose the residual data, which is ordinarily fed right back -- to the continuation upon execution of the request. This allows certain -- control operations (such as seek and tell) to flush, check the length -- of, or adjust the residual data. data CtlArg t m a CtlArg :: !carg -> (CtlRes cres -> Iter t m a) -> (Chunk t) -> CtlArg t m a -- | Contains information about a failed Iter. Failures of type -- IterException must be caught by catchI (or tryI, -- etc.). However, any other type of failure is considered a parse error, -- and will be caught by multiParse, ifParse, and -- mplus. data IterFail -- | An actual error occured that is not a parse error, EOF, etc. IterException :: !SomeException -> IterFail -- | List of (input_seen, input_expected) pairs. IterExpected :: [(String, String)] -> IterFail -- | An EOF error occurred, either in some IO action wrapped by -- liftIO, or in some Iter that called throwEOFI. IterEOFErr :: IOError -> IterFail -- | A miscellaneous parse error occured. IterParseErr :: String -> IterFail -- | What you get from mzero. Useful if you don't want to specify -- any information about the failure. IterMzero :: IterFail -- | An IterR is the result of feeding a chunk of data to an -- Iter. An IterR is in one of several states: it may -- require more input (IterF), it may wish to execute monadic -- actions in the transformed monad (IterM), it may have a control -- request for an enclosing enumerator (IterC), it may have -- produced a result (Done), or it may have failed (Fail). data IterR t m a -- | The iteratee requires more input. IterF :: !Iter t m a -> IterR t m a -- | The iteratee must execute monadic bind in monad m IterM :: !m (IterR t m a) -> IterR t m a -- | A control request (see CtlArg). IterC :: !CtlArg t m a -> IterR t m a -- | Sufficient input was received; the Iter is returning a result -- of type a. In adition, the IterR has a Chunk -- containing any residual input that was not consumed in producing the -- result. Done :: a -> (Chunk t) -> IterR t m a -- | The Iter failed. If it was an enumerator, the target -- Iter that the enumerator was feeding likely has not failed, in -- which case its current state is returned in the Maybe a. If -- it makes sense to preserve the state of the input stream (which it -- does for most errors except parse errors), then the third parameter -- includes the residual Chunk at the time of the failure. Fail :: !IterFail -> !Maybe a -> !Maybe (Chunk t) -> IterR t m a -- | Builds an Iter that keeps requesting input until it receives a -- non-null Chunk. In other words, the Chunk fed to -- the argument function is guaranteed either to contain data or to have -- the EOF flag true (or both). iterF :: ChunkData t => (Chunk t -> IterR t m a) -> Iter t m a -- | True if an IterR is requesting something from an -- enumerator--i.e., the IterR is not Done or Fail. isIterActive :: IterR t m a -> Bool -- | Show the current state of an IterR, prepending it to some -- remaining input (the standard ShowS optimization), when -- a is in class Show. Note that if a is not in -- Show, you can simply use the shows function. iterShows :: (ChunkData t, Show a) => IterR t m a -> ShowS -- | Show the current state of an Iter if type a is in the -- Show class. (Otherwise, you can simply use the ordinary -- show function.) iterShow :: (ChunkData t, Show a) => IterR t m a -> String -- | Feed an EOF to an Iter and return the result. Throws an -- exception if there has been a failure. run :: (ChunkData t, Monad m) => Iter t m a -> m a -- | Runs an Iter from within a different Iter monad. If -- successful, runI iter will produce the same result as -- lift (run iter). However, if iter -- fails, run throws a language-level exception, which cannot be -- caught within other Iter monads. By contrast, runI -- throws a monadic exception that can be caught. In short, use -- runI in preference to run in situations where both -- are applicable. See a more detailed discussion of the same issue with -- examples in the documentation for .|$ in -- Data.IterIO.Inum. runI :: (ChunkData t1, ChunkData t2, Monad m) => Iter t1 m a -> Iter t2 m a -- | Make an IterEOFErr from a String. mkIterEOF :: String -> IterFail -- | Exception thrown by CtlI when the type of the control request -- is not supported by the enclosing enumerator. data IterCUnsupp IterCUnsupp :: carg -> IterCUnsupp -- | Throw an exception from an Iteratee. The exception will be propagated -- properly through nested Iteratees, which will allow it to be -- categorized properly and avoid situations in which resources such as -- file handles are not released. (Most iteratee code does not assume the -- Monad parameter m is in the MonadIO class, and hence -- cannot use catch or onException to clean up -- after exceptions.) Use throwI in preference to throw -- whenever possible. -- -- Do not use throwI to throw parse errors or EOF errors. Use -- throwEOFI and throwParseI instead. For performance -- reasons, the IterFail type segregates EOF and parse errors from -- other types of failures. throwI :: Exception e => e -> Iter t m a -- | Throw an exception of type IterEOF. This will be interpreted -- by mkInum as an end of file chunk when thrown by the codec. -- It will also be interpreted by ifParse and multiParse as -- parsing failure. If not caught within the Iter monad, the -- exception will be rethrown by run (and hence |$) as an -- IOError of type EOF. throwEOFI :: String -> Iter t m a -- | Throw a miscellaneous parse error (after which input is assumed to be -- unsynchronized and thus is discarded). Parse errors may be caught as -- exception type IterFail, but they can also be caught more -- efficiently by the functions multiParse, ifParse, and -- mplus. throwParseI :: String -> Iter t m a -- | Catch an exception thrown by an Iter, including exceptions -- thrown by any Inums fused to the Iter (or applied to -- it with .|$). If you wish to catch just errors thrown within -- Inums, see the function inumCatch in -- Data.IterIO.Inum. -- -- On exceptions, catchI invokes a handler passing it both the -- exception thrown and the state of the failing IterR, which may -- contain more information than just the exception. In particular, if -- the exception occured in an Inum, the returned IterR -- will also contain the IterR being fed by that Inum, -- which likely will not have failed. To avoid discarding this extra -- information, you should not re-throw exceptions with throwI. -- Rather, you should re-throw an exception by re-executing the failed -- IterR with reRunIter. For example, a possible definition -- of onExceptionI is: -- --
-- onExceptionI iter cleanup = -- iter `catchI` \(SomeException _) r -> cleanup >> reRunIter r ---- -- Note that catchI only works for synchronous -- exceptions, such as IO errors (thrown within liftIO blocks), -- the monadic fail operation, and exceptions raised by -- throwI. It is not possible to catch asynchronous -- exceptions, such as lazily evaluated divide-by-zero errors, the -- throw function, or exceptions raised by other threads using -- throwTo if those exceptions might arrive anywhere -- outside of a liftIO call. -- -- `catchI` has the default infix precedence (infixl 9 -- `catchI`), which binds more tightly than any concatenation or -- fusing operators. catchI :: (Exception e, ChunkData t, Monad m) => Iter t m a -> (e -> IterR t m a -> Iter t m a) -> Iter t m a -- | If an Iter succeeds and returns a, returns -- Right a. If the Iter fails and throws an -- exception e (of type e), returns Left (e, -- r) where r is the state of the failing Iter. tryI :: (ChunkData t, Monad m, Exception e) => Iter t m a -> Iter t m (Either (e, IterR t m a) a) -- | A varient of tryI that returns the IterFail state rather -- than trying to match a particular exception. tryFI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m (Either IterFail a) -- | A version of tryI that catches all exceptions. Instead of -- returning the exception caught, it returns the failing IterR -- (from which you can extract the exception if you really want it). The -- main use of this is for doing some kind of clean-up action, then -- re-throwing the exception with reRunIter. -- -- For example, the following is a possible implementation of -- finallyI: -- --
-- finallyI iter cleanup = do -- er <- tryRI iter -- cleanup -- either reRunIter return er --tryRI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m (Either (IterR t m a) a) -- | A variant of tryI that just catches EOF errors. Returns -- Nothing after an EOF error, and Just the result -- otherwise. Should be much faster than trying to catch an EOF error of -- type Exception. tryEOFI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m (Maybe a) -- | Execute an Iter, then perform a cleanup action regardless of -- whether the Iter threw an exception or not. Analogous to the -- standard library function finally. finallyI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m b -> Iter t m a -- | Execute an Iter and perform a cleanup action if the Iter -- threw an exception. Analogous to the standard library function -- onException. onExceptionI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m b -> Iter t m a -- | Simlar to tryI, but saves all data that has been fed to the -- Iter, and rewinds the input if the Iter fails. (The -- B in tryBI stands for "backtracking".) Thus, if -- tryBI returns Left exception, the next -- Iter to be invoked will see the same input that caused the -- previous Iter to fail. (For this reason, it makes no sense ever -- to call resumeI on the Iter you get back from -- tryBI, which is why tryBI does not return the -- failing Iteratee the way tryI does.) -- -- Because tryBI saves a copy of all input, it can consume a lot -- of memory and should only be used when the Iter argument is -- known to consume a bounded amount of data. tryBI :: (ChunkData t, Monad m, Exception e) => Iter t m a -> Iter t m (Either e a) -- | A variant of tryBI that, also rewinds input on failure, but -- returns the raw IterFail structure, rather than mapping it to a -- particular exception. This is much faster because it requires no -- dynamic casts. However, the same warning applies that tryFBI -- should not be applied to Iters that could take unbounded input. tryFBI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m (Either IterFail a) -- | ifParse iter success failure runs iter, but saves a -- copy of all input consumed using tryFBI. (This means -- iter must not consume unbounded amounts of input! See -- multiParse for such cases.) If iter succeeds, its -- result is passed to the function success. If iter -- throws a parse error (with throwParseI), throws an EOF error -- (with throwEOFI), or executes mzero, then -- failure is executed with the input re-wound (so that -- failure is fed the same input that iter was). If -- iter throws any other type of exception, ifParse -- passes the exception back and does not execute failure. -- -- See Data.IterIO.Parse for a discussion of this function and the -- related infix operator \/ (which is a synonym for -- ifNoParse). ifParse :: (ChunkData t, Monad m) => Iter t m a -> (a -> Iter t m b) -> Iter t m b -> Iter t m b -- | ifNoParse is just ifParse with the second and third -- arguments reversed. ifNoParse :: (ChunkData t, Monad m) => Iter t m a -> Iter t m b -> (a -> Iter t m b) -> Iter t m b -- | Try two Iteratees and return the result of executing the second if the -- first one throws a parse, EOF, or mzero error. Note that -- Data.IterIO.Parse defines <|> as an -- infix synonym for this function. -- -- The statement multiParse a b is similar to ifParse -- a return b, but the two functions operate differently. Depending -- on the situation, only one of the two formulations may be correct. -- Specifically: -- --
-- total :: (Monad m) => Iter String m Int -- total = multiParse parseAndSumIntegerList (return -1) -- Bad ---- -- Here the intent is for parseAndSumIntegerList to parse a -- (possibly huge) list of integers and return their sum. If there is a -- parse error at any point in the input, then the result is identical to -- having defined total = return -1. But return -1 -- succeeds immediately, consuming no input, which means that -- total must return all left-over input for the next action -- (i.e., next in total >>= next). Since -- total has to look arbitrarily far into the input to determine -- that parseAndSumIntegerList fails, in practice total -- will have to save all input until it knows that -- parseAndSumIntegerList succeeds. -- -- A better approach might be: -- --
-- total = multiParse parseAndSumIntegerList (nullI >> return -1) ---- -- Here nullI discards all input until an EOF is encountered, so -- there is no need to keep a copy of the input around. This makes sense -- so long as total is the last or only Iteratee run on the -- input stream. (Otherwise, nullI would have to be replaced with -- an Iteratee that discards input up to some end-of-list marker.) -- -- Another approach might be to avoid parsing combinators entirely and -- use: -- --
-- total = parseAndSumIntegerList `catchI` handler -- where handler (IterNoParse _) _ = return -1 ---- -- This last definition of total may leave the input in some -- partially consumed state. This is fine so long as total is -- the last Iter executed on the input stream. Otherwise, before -- throwing the parse error, parseAndSumIntegerList would need -- to ensure the input is at some reasonable boundary point for whatever -- comes next. (The ungetI function is sometimes helpful for this -- purpose.) multiParse :: (ChunkData t, Monad m) => Iter t m a -> Iter t m a -> Iter t m a -- | Sinks data like /dev/null, returning () on EOF. nullI :: (Monad m, ChunkData t) => Iter t m () -- | Returns a non-empty amount of input data if there is any input left. -- Returns mempty on an EOF condition. data0I :: ChunkData t => Iter t m t -- | Like data0I, but always returns non-empty data. Throws an -- exception on an EOF condition. dataI :: ChunkData t => Iter t m t -- | A variant of data0I that reads the whole input up to an EOF and -- returns it. pureI :: (Monad m, ChunkData t) => Iter t m t -- | Returns the next Chunk that either contains non-null -- data or has the EOF bit set. chunkI :: (Monad m, ChunkData t) => Iter t m (Chunk t) -- | Keep running an Iter until either its output is not null -- or we have reached EOF. Return the the Iter's value on the last -- (i.e., usually non-null) iteration. whileNullI :: (ChunkData tIn, ChunkData tOut, Monad m) => Iter tIn m tOut -> Iter tIn m tOut -- | Runs an Iter then rewinds the input state, so that the effect -- is to parse lookahead data. (See tryBI if you want to rewind -- the input only when the Iter fails.) peekI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m a -- | Does not actually consume any input, but returns True if there -- is no more input data to be had. atEOFI :: (Monad m, ChunkData t) => Iter t m Bool -- | Place data back onto the input stream, where it will be the next data -- consumed by subsequent Iters. ungetI :: ChunkData t => t -> Iter t m () -- | Issue a control request. Returns CtlUnsupp if the request type -- is unsupported. Otherwise, returns CtlDone with the result if -- the request succeeds, or return CtlFail if the request -- type is supported but attempting to execute the request caused an -- exception. safeCtlI :: (CtlCmd carg cres, Monad m) => carg -> Iter t m (CtlRes cres) -- | Issue a control request and return the result. Throws an exception of -- type IterCUnsupp if the operation type was not supported by an -- enclosing enumerator. ctlI :: (CtlCmd carg cres, ChunkData t, Monad m) => carg -> Iter t m cres -- | Run an Iter until it enters the Done or Fail -- state, then use a function to transform the IterR. onDone :: Monad m => (IterR t m a -> IterR t m b) -> Iter t m a -> Iter t m b -- | fmapI is like liftM, but differs in one important -- respect: it preserves the failed result of an enumerator (and in fact -- applies the function to the non-failed target Iter state). By -- contrast, liftM, which is equivalent to liftM f i = -- i >>= return . f, transforms the -- Maybe a component of all Fail states to -- Nothing because of its use of >>=. fmapI :: Monad m => (a -> b) -> Iter t m a -> Iter t m b -- | The equivalent of onDone for IterRs. onDoneR :: Monad m => (IterR t m a -> IterR t m b) -> IterR t m a -> IterR t m b -- | Step an active IterR (i.e., one in the IterF, -- IterM, or IterC state) to its next state, and pass the -- result through a function. stepR :: Monad m => IterR t m a -> (IterR t m a -> IterR t m b) -> IterR t m b -> IterR t m b -- | A variant of stepR that only works for the IterF and -- IterC states, not the IterM state. (Because of this -- additional restriction, the input and output Monad types -- m1 and m2 do not need to be the same.) stepR' :: IterR t m1 a -> (IterR t m1 a -> IterR t m2 b) -> IterR t m2 b -> IterR t m2 b -- | The equivalent for runI for IterRs. runR :: (ChunkData t1, ChunkData t2, Monad m) => IterR t1 m a -> IterR t2 m a -- | Maps the result of an IterR like fmap, but only if the -- IterR is no longer active. It is an error to call this function -- on an IterR in the IterF, IterM, or IterC -- state. Because of this restriction, fmapR does not require -- the input and output Monad types (m1 and m2) -- to be the same. fmapR :: (a -> b) -> IterR t m1 a -> IterR t m2 b -- | Turn an IterR back into an Iter. reRunIter :: (ChunkData t, Monad m) => IterR t m a -> Iter t m a -- | Feed more input to an Iter that has already been run (and hence -- is already an IterR). In the event that the IterR is -- requesting more input (i.e., is in the IterF state), this is -- straight forward. However, if the Iter is in some other state -- such as IterM, this function needs to save the input until such -- time as the IterR is stepped to a new state (e.g., with -- stepR or reRunIter). runIterR :: (ChunkData t, Monad m) => IterR t m a -> Chunk t -> IterR t m a -- | Get the residual data for an IterR that is in no longer active -- or that is in the IterC state. (It is an error to call this -- function on an IterR in the IterF or IterM -- state.) getResid :: ChunkData t => IterR t m a -> Chunk t -- | Set residual data for an IterR that is not active. (It is an -- error to call this on an IterR in the Done, -- IterM, or IterC states.) setResid :: IterR t1 m1 a -> Chunk t2 -> IterR t2 m2 a instance Typeable1 Chunk instance Typeable1 CtlRes instance Typeable IterFail instance Typeable IterCUnsupp instance Eq t => Eq (Chunk t) instance Exception IterCUnsupp instance Show IterCUnsupp instance MonadIO m => MonadFix (Iter t m) instance MonadIO m => MonadIO (Iter t m) instance MonadTrans (Iter t) instance (ChunkData t, Monad m) => MonadPlus (Iter t m) instance Monad m => Monad (Iter t m) instance Monad m => Applicative (Iter t m) instance Monad m => Functor (IterR t m) instance Monad m => Functor (Iter t m) instance (Typeable t, Typeable1 m, Typeable a) => Typeable (Iter t m a) instance (Typeable t, Typeable1 m) => Typeable1 (Iter t m) instance ChunkData t => Show (IterR t m a) instance Exception IterFail instance Show IterFail instance ChunkData t => ChunkData (Chunk t) instance ChunkData t => Monoid (Chunk t) instance Functor Chunk instance ChunkData t => Show (Chunk t) instance ChunkData () instance ChunkData ByteString instance ChunkData ByteString instance Show a => ChunkData [a] -- | This module contains various helper functions and instances for using -- Iters of different Monads together in the same pipeline. -- For example, as-is the following code is illegal: -- --
-- iter1 :: Iter String IO Bool -- iter1 = ... -- -- iter2 :: Iter String (StateT MyState IO) () -- iter2 = do ... -- s <- iter1 -- ILLEGAL: iter1 is in wrong monad -- ... ---- -- You can't invoke iter1 from within iter2 because the -- Iter type is wrapped around a different Monad in each -- case. However, the function liftI exactly solves this problem: -- --
-- s <- liftI iter1 ---- -- Conversely, you may be in a Monad like Iter String -- IO and need to invoke a computation that requires some other -- monad functionality, such as a reader. There are a number of -- iteratee-specific runner functions that help you run other -- MonadTrans transformers inside the Iter monad. These -- typically use the names of the runner functions in the mtl library, -- but with an I appended--for instance runReaderTI, -- runStateTI, runWriterTI. Here's a fuller example of -- adapting the inner Iter Monad. The example also -- illustrates that Iter t m is member any mtl classes -- (such as MonadReader and MonadState) that m is. -- --
-- iter1 :: Iter String (ReaderT MyState IO) Bool -- iter1 = do -- s <- ask -- liftIO $ (putStrLn (show s) >> return True) -- `catch` (SomeException _) -> return False -- -- iter2 :: Iter String (StateT MyState IO) () -- iter2 = do -- s <- get -- ok <- liftI $ runReaderTI iter1 s -- if ok then return () else fail "iter1 failed" --module Data.IterIO.Trans -- | Run an Iter s m computation from witin the -- Iter s (t m) monad, where t is a -- MonadTrans. liftI :: (MonadTrans t, Monad m, Monad (t m), ChunkData s) => Iter s m a -> Iter s (t m) a -- | Run an Iter t IO computation from within an -- Iter t m monad where m is in class -- MonadIO. liftIterIO :: (ChunkData t, MonadIO m) => Iter t IO a -> Iter t m a -- | Turn a computation of type Iter t (ContT -- (Iter t m a) m) a into one of type Iter t m -- a. Note the continuation has to return type Iter t m -- a and not a so that runContTI can call itself -- recursively. runContTI :: (ChunkData t, Monad m) => Iter t (ContT (Iter t m a) m) a -> Iter t m a -- | Run a computation of type Iter t (ErrorT e m) -- from within the Iter t m monad. This function is here -- for completeness, but please consider using throwI instead, -- since the Iter monad already has built-in exception handling -- and it's best to have a single, uniform approach to error reporting. runErrorTI :: (Monad m, ChunkData t, Error e) => Iter t (ErrorT e m) a -> Iter t m (Either e a) -- | Run an Iter t (ListT m) computation from within -- the Iter t m monad. runListTI :: (Monad m, ChunkData t) => Iter t (ListT m) a -> Iter t m [a] -- | Run an Iter t (ReaderT r m) computation from -- within the Iter t m monad. runReaderTI :: (ChunkData t, Monad m) => Iter t (ReaderT r m) a -> r -> Iter t m a -- | Run an Iter t (RWST r w s m) computation from -- within the Iter t m monad. runRWSI :: (ChunkData t, Monoid w, Monad m) => Iter t (RWST r w s m) a -> r -> s -> Iter t m (a, s, w) -- | Run an Iter t (RWST r w s m) computation from -- within the Iter t m monad. Just like runRWSI, -- execpt this function is for Lazy RWST rather than strict -- RWST. runRWSLI :: (ChunkData t, Monoid w, Monad m) => Iter t (RWST r w s m) a -> r -> s -> Iter t m (a, s, w) -- | Run an Iter t (StateT m) computation from -- within the Iter t m monad. runStateTI :: (ChunkData t, Monad m) => Iter t (StateT s m) a -> s -> Iter t m (a, s) -- | Run an Iter t (StateT m) computation from -- within the Iter t m monad. Just like -- runStateTI, except this function works on Lazy -- StateT rather than strict StateT. runStateTLI :: (ChunkData t, Monad m) => Iter t (StateT s m) a -> s -> Iter t m (a, s) -- | Run an Iter t (WriterT w m) computation from -- within the Iter t m monad. runWriterTI :: (ChunkData t, Monoid w, Monad m) => Iter t (WriterT w m) a -> Iter t m (a, w) -- | Run an Iter t (WriterT w m) computation from -- within the Iter t m monad. This is the same as -- runWriterT but for the Lazy WriterT, rather than -- the strict one. runWriterTLI :: (ChunkData t, Monoid w, Monad m) => Iter t (WriterT w m) a -> Iter t m (a, w) -- | Adapt an Iter from one monad to another. This function is the -- lowest-level monad adapter function, upon which all of the other -- adapters are built. adaptIter requires two functions as -- arguments. One adapts the result to a new type (if required). The -- second adapts monadic computations from one monad to the other. For -- example, liftI could be implemented as: -- --
-- liftI :: (MonadTrans t, Monad m, Monad (t m), ChunkData s) => -- Iter s m a -> Iter s (t m) a -- liftI = adaptIter id (\m -> lift (lift m) >>= liftI) ---- -- Here lift (lift m) executes a computation -- m of type m (Iter s m a) from within the -- Iter s (t m) monad. The result, of type -- Iter s m a, can then be fed back into liftI -- recursively. -- -- Note that in general a computation adapters must invoke the outer -- adapter function recursively. adaptIter is designed this way -- because the result adapter function may need to change. An example is -- runStateTI, which could be implemented as follows: -- --
-- runStateTI :: (ChunkData t, Monad m) => -- Iter t (StateT s m) a -> s -> Iter t m (a, s) -- runStateTI iter s = adaptIter adaptResult adaptComputation iter -- where adaptResult a = (a, s) -- adaptComputation m = do (r', s') <- lift (runStateT m s) -- runStateTI r' s' ---- -- Here, after executing runStateT, the state may be modified. -- Thus, adaptComputation invokes runStateTI -- recursively with the modified state, s', to ensure that -- subsequent IterM computations will be run on the latest state, -- and that eventually adaptResult will pair the result -- a with the newest state. adaptIter :: (ChunkData t, Monad m1) => (a -> b) -> (m1 (Iter t m1 a) -> Iter t m2 b) -> Iter t m1 a -> Iter t m2 b -- | Simplified adapter function to translate Iter computations from -- one monad to another. This only works on monads m for which -- running m a returns a result of type a. For more -- complex scenarios (such as ListT or StateT), you need to -- use the more general adaptIter. -- -- As an example, the liftIterIO function is implemented as -- follows: -- --
-- liftIterIO :: (ChunkData t, MonadIO m) => Iter t IO a -> Iter t m a -- liftIterIO = adaptIterM liftIO --adaptIterM :: (ChunkData t, Monad m1, Monad m2) => (m1 (Iter t m1 a) -> m2 (Iter t m1 a)) -> Iter t m1 a -> Iter t m2 a -- | IterStateT is a variant of the StateT monad -- transformer specifically designed for use inside Iters. The -- IterStateT Monad itself is the same as StateT. However, -- the runIterStateT function works differently from -- runStateT--it returns an IterR and the result state -- separately. The advantage of this approach is that you can still -- recover the state at the point of the excaption even after an -- IterFail or InumFail condition. newtype IterStateT s m a IterStateT :: (s -> m (a, s)) -> IterStateT s m a -- | Runs an IterStateT s m computation on some state -- s. Returns the result (IterR) of the Iter and -- the state of s as a pair. Pulls residual input up to the -- enclosing Iter monad (as with pullupResid in -- Data.IterIO.Inum). runIterStateT :: (ChunkData t, Monad m) => Iter t (IterStateT s m) a -> s -> Iter t m (IterR t m a, s) -- | Returns the state in an Iter t (IterStateT s m) -- monad. Analogous to get for a StateT s -- m monad. iget :: Monad m => Iter t (IterStateT s m) s -- | Returns a particular field of the IterStateT state, analogous -- to gets for StateT. igets :: Monad m => (s -> a) -> Iter t (IterStateT s m) a -- | Sets the IterStateT state. Analogous to put for -- StateT. iput :: Monad m => s -> Iter t (IterStateT s m) () -- | Modifies the IterStateT state. Analogous to -- modify for StateT. imodify :: Monad m => (s -> s) -> Iter t (IterStateT s m) () instance MonadWriter w m => MonadWriter w (IterStateT s m) instance MonadReader r m => MonadReader r (IterStateT s m) instance MonadError e m => MonadError e (IterStateT s m) instance MonadCont m => MonadCont (IterStateT s m) instance (Monoid w, MonadWriter w m, ChunkData t) => MonadWriter w (Iter t m) instance (MonadState s m, ChunkData t) => MonadState s (Iter t m) instance (MonadReader r m, ChunkData t) => MonadReader r (Iter t m) instance (Error e, MonadError e m, ChunkData t) => MonadError e (Iter t m) instance (ChunkData t, MonadCont m) => MonadCont (Iter t m) instance MonadIO m => MonadIO (IterStateT s m) instance MonadTrans (IterStateT s) instance Monad m => Monad (IterStateT s m) module Data.IterIO.Inum -- | The type of an iterator-enumerator, which transcodes data from -- some input type tIn to some output type tOut. An -- Inum acts as an Iter when consuming data, then acts as -- an enumerator when feeding transcoded data to another Iter. -- -- At a high level, one can think of an Inum as a function from -- Iters to IterRs, where an Inum's input and -- output types are different. A simpler-seeming alternative to -- Inum might have been: -- --
-- type Inum' tIn tOut m a = Iter tOut m a -> Iter tIn m a ---- -- In fact, given an Inum object inum, it is possible -- to construct a function of type Inum' with (inum -- .|). But sometimes one might like to concatenate -- Inums. For instance, consider a network protocol that changes -- encryption or compression modes midstream. Transcoding is done by -- Inums. To change transcoding methods after applying an -- Inum to an iteratee requires the ability to "pop" the -- iteratee back out of the Inum so as to be able to hand it to -- another Inum. Inum's return type (Iter tIn m -- (IterR tOut m a) as opposed to Iter tIn m a) allows the -- monadic bind operator >>= to accomplish this popping in -- conjunction with the tryRI and reRunIter functions. -- -- All Inums must obey the following two rules. -- --
-- inum |$ iter = run (inum .| iter) -- infixr 2 |$ --(|$) :: (ChunkData t, Monad m) => Onum t m a -> Iter t m a -> m a -- | .|$ is a variant of |$ that allows you to apply an -- Onum from within an Iter monad. This is often useful in -- conjuction with enumPure, if you want to parse at some -- coarse-granularity (such as lines), and then re-parse the contents of -- some coarser-grained parse unit. For example: -- --
-- rawcommand <- lineI
-- command <- enumPure rawcommand .|$ parseCommandI
-- return Request { cmd = command, rawcmd = rawcommand }
--
--
-- .|$ has the same fixity as |$, namely:
--
-- -- infixr 2 .|$ ---- -- Note the important distinction between (.|$) and -- (.|). (.|$) runs an Onum and does not -- touch the current input, while (.|) pipes the current input -- through an Inum. For instance, to send the contents of a file -- to standard output (regardless of the current input), you must say -- enumFile ".signature" .|$ stdoutI. But to -- take the current input, compress it, and send the result to standard -- output, you must use .|, as in inumGzip .| -- stdoutI. -- -- As suggested by the types, enum .|$ iter is sort of -- equivalent to lift (enum |$ iter), except that the -- latter will call throw on failures, causing language-level -- exceptions that cannot be caught within the outer Iter. Thus, -- it is better to use .|$ than lift (... |$ -- ...), though in the less general case of the IO monad, enum -- .|$ iter is equivalent to liftIO (enum |$ -- iter) as illustrated by the following examples: -- --
-- -- Catches exception, because .|$ propagates failure through the outer -- -- Iter Monad, where it can still be caught. -- apply1 :: IO String -- apply1 = enumPure "test1" |$ iter `catchI` handler -- where -- iter = enumPure "test2" .|$ fail "error" -- handler (SomeException _) _ = return "caught error" -- -- -- Does not catch error. |$ turns the Iter failure into a language- -- -- level exception, which can only be caught in the IO Monad. -- apply2 :: IO String -- apply2 = enumPure "test1" |$ iter `catchI` handler -- where -- iter = lift (enumPure "test2" |$ fail "error") -- handler (SomeException _) _ = return "caught error" -- -- -- Catches the exception, because liftIO uses the IO catch function to -- -- turn language-level exceptions into monadic Iter failures. (By -- -- contrast, lift works in any Monad, so cannot do this in apply2.) -- -- This example illustrates how liftIO is not equivalent to lift. -- apply3 :: IO String -- apply3 = enumPure "test1" |$ iter `catchI` handler -- where -- iter = liftIO (enumPure "test2" |$ fail "error") -- handler (SomeException _) _ = return "caught error" --(.|$) :: (ChunkData tIn, ChunkData tOut, Monad m) => Onum tOut m a -> Iter tOut m a -> Iter tIn m a -- | Concatenate the outputs of two enumerators. For example, -- enumFile "file1" `cat` enumFile "file2" -- produces an Onum that outputs the concatenation of files -- "file1" and "file2". Unless the first Inum fails, cat -- always invokes the second Inum, as the second Inum may -- have monadic side-effects that must be executed even when the -- Iter has already finished. See lcat if you want to stop -- when the Iter no longer requires input. If you want to continue -- executing even in the event of an InumFail condition, you can -- wrap the first Inum with inumCatch and invoke -- resumeI from within the exception handler. -- -- cat (and lcat, described below) are useful in right -- folds. Say, for instance, that files is a list of files you -- wish to concatenate. You can use a construct such as: -- --
-- catFiles :: (MonadIO m) => [FilePath] -> Onum L.ByteString m a -- catFiles files = foldr (cat . enumFile) inumNull files ---- -- Note the use of inumNull as the starting value for -- foldr. This is not to be confused with inumNop. -- inumNull acts as a no-op for concatentation, producing no -- output analogously to /dev/null. By contrast inumNop -- is the no-op for fusing (see |. and .| below) because it -- passes all data through untouched. -- -- cat has fixity: -- --
-- infixr 3 `cat` --cat :: (ChunkData tIn, ChunkData tOut, Monad m) => Inum tIn tOut m a -> Inum tIn tOut m a -> Inum tIn tOut m a -- | Lazy cat. Like cat, except that it does not run the second -- Inum if the Iter is no longer active after completion of -- the first Inum. Also has fixity infixr 3 `lcat`. lcat :: (ChunkData tIn, ChunkData tOut, Monad m) => Inum tIn tOut m a -> Inum tIn tOut m a -> Inum tIn tOut m a -- | Left-associative pipe operator. Fuses two Inums when the output -- type of the first Inum is the same as the input type of the -- second. More specifically, if inum1 transcodes type -- tIn to tOut and inum2 transcodes -- tOut to tOut2, then inum1 |. inum2 produces -- a new Inum that transcodes from tIn to tOut2. -- -- Typically types i and iR are Iter tOut2 m -- a and IterR tOut2 m a, respectively, in which -- case the second argument and result of |. are also -- Inums. -- -- This function is equivalent to: -- --
-- outer |. inner = \iter -> outer .| inner iter -- infixl 4 |. ---- -- But if you like point-free notation, think of it as outer |. inner -- = (outer .|) . inner, or better yet (|.) = (.) . -- (.|). (|.) :: (ChunkData tIn, ChunkData tOut, Monad m) => Inum tIn tOut m iR -> (i -> Iter tOut m iR) -> (i -> Iter tIn m iR) -- | Right-associative pipe operator. Fuses an Inum that transcodes -- tIn to tOut with an Iter taking input type -- tOut to produce an Iter taking input type -- tIn. If the Iter is still active when the Inum -- terminates (either normally or through an exception), then .| -- sends it an EOF. -- -- Has fixity: -- --
-- infixr 4 .| --(.|) :: (ChunkData tIn, ChunkData tOut, Monad m) => Inum tIn tOut m a -> Iter tOut m a -> Iter tIn m a -- | Catches errors thrown by an Inum, or a set of fused -- Inums. Note that only errors in Inums that are lexically -- within the scope of the argument to inumCatch will be caught. -- For example: -- --
-- inumBad :: (ChunkData t, Monad m) => Inum t t m a -- inumBad = mkInum $ fail "inumBad" -- -- skipError :: (ChunkData tIn, MonadIO m) => -- SomeException -- -> IterR tIn m (IterR tOut m a) -- -> Iter tIn m (IterR tOut m a) -- skipError e iter = do -- liftIO $ hPutStrLn stderr $ "skipping error: " ++ show e -- resumeI iter -- -- -- Throws an exception, because inumBad was fused outside the argument -- -- to inumCatch. -- test1 :: IO () -- test1 = inumCatch (enumPure "test") skipError |. inumBad |$ nullI -- -- -- Does not throw an exception, because inumBad fused within the -- -- argument to inumCatch. -- test2 :: IO () -- test2 = inumCatch (enumPure "test" |. inumBad) skipError |$ nullI -- -- -- Again no exception, because inumCatch is wrapped around inumBad. -- test3 :: IO () -- test3 = enumPure "test" |. inumCatch inumBad skipError |$ nullI ---- -- Note that `inumCatch` has the default infix precedence -- (infixl 9 `inumcatch`), which binds more tightly than any -- concatenation or fusing operators. -- -- As noted for catchI, exception handlers receive both the -- exception thrown and the failed IterR. Particularly in the case -- of inumCatch, it is important to re-throw exceptions by -- re-executing the failed Iter with reRunIter, not passing -- the exception itself to throwI. That way, if the exception is -- re-caught, resumeI will continue to work properly. For example, -- to copy two files to standard output and ignore file not found errors -- but re-throw any other kind of error, you could use the following: -- --
-- resumeTest :: IO () -- resumeTest = doFile "file1" `cat` doFile "file2" |$ stdoutI -- where -- doFile path = inumCatch (enumFile' path) $ \err r -> -- if isDoesNotExistError err -- then verboseResumeI r -- else reRunIter r --inumCatch :: (Exception e, ChunkData tIn, Monad m) => Inum tIn tOut m a -> (e -> IterR tIn m (IterR tOut m a) -> Iter tIn m (IterR tOut m a)) -> Inum tIn tOut m a -- | Execute some cleanup action when an Inum finishes. inumFinally :: (ChunkData tIn, Monad m) => Inum tIn tOut m a -> Iter tIn m b -> Inum tIn tOut m a -- | Execute some cleanup action if an Inum fails. Does not execute -- the action if the Iter (or some inner Inum) fails. Has -- the same scoping rules as inumCatch. inumOnException :: (ChunkData tIn, Monad m) => Inum tIn tOut m a -> Iter tIn m b -> Inum tIn tOut m a -- | Used in an exception handler, after an Inum failure, to resume -- processing of the Iter by the next enumerator in a cated -- series. See inumCatch for an example. resumeI :: (ChunkData tIn, Monad m) => IterR tIn m (IterR tOut m a) -> Iter tIn m (IterR tOut m a) -- | Like resumeI, but if the Iter is resumable, also prints -- an error message to standard error before resuming. verboseResumeI :: (ChunkData tIn, MonadIO m) => IterR tIn m (IterR tOut m a) -> Iter tIn m (IterR tOut m a) -- | A ResidHandler specifies how to handle residual data in an -- Inum. Typically, when an Inum finishes executing, there -- are two kinds of residual data. First, the Inum itself (in its -- role as an iteratee) may have left some unconsumed data. Second, the -- target Iter being fed by the Inum may have some resitual -- data, and this data may be of a different type. A -- ResidHandler allows this residual data to be adjusted by -- untranslating the residual data of the target Iter and sticking -- the result back into the Inum's residual data. -- -- The two most common ResidHandlers are pullupResid (to -- pull the target Iter's residual data back up to the Inum -- as is), and id (to do no adjustment of residual data). -- -- ResidHandlers are used by the mkInumC function, and by -- the passCtl CtlHandler. type ResidHandler tIn tOut = (tIn, tOut) -> (tIn, tOut) -- | A control handler maps control requests to IterR results. -- Generally the type parameter m1 is Iter t' m. type CtlHandler m1 t m a = CtlArg t m a -> m1 (IterR t m a) -- | Create a stateless Inum from a "codec" Iter that -- transcodes the input type to the output type. The codec is invoked -- repeately until one of the following occurs: The codec returns -- null data, the codec throws an exception, or the underlying -- target Iter is no longer active. If the codec throws an -- exception of type IterEOF, this is considered normal -- termination and the error is not further propagated. -- -- mkInumC requires two other arguments before the codec. First, -- a ResidHandler allows residual data to be adjusted between the -- input and output Iter monads. Second, a CtlHandler -- specifies a handler for control requests. For example, to pass up -- control requests and ensure no residual data is lost when the -- Inum is fused to an Iter, the inumConcat -- function given previously for mkInum at #mkInumExample -- could be re-written: -- --
-- inumConcat :: (Monad m) => Inum [L.ByteString] L.ByteString m a -- inumConcat = mkInumC reList (passCtl reList) iterConcat -- where reList (a, b) = (b:a, mempty) --mkInumC :: (ChunkData tIn, ChunkData tOut, Monad m) => ResidHandler tIn tOut -> CtlHandler (Iter tIn m) tOut m a -> Iter tIn m tOut -> Inum tIn tOut m a -- | Create an Inum based on an Iter that transcodes the -- input to the output type. This is a simplified version of -- mkInumC that rejects all control requests and does not adjust -- residual data. -- --
-- mkInum = mkInumC id noCtl --mkInum :: (ChunkData tIn, ChunkData tOut, Monad m) => Iter tIn m tOut -> Inum tIn tOut m a -- | A simplified version of mkInum that passes all control requests -- to enclosing enumerators. It requires a ResidHandler to -- describe how to adjust residual data. (E.g., use pullupResid -- when tIn and tOut are the same type.) -- --
-- mkInumP adj = mkInumC adj (passCtl adj) --mkInumP :: (ChunkData tIn, ChunkData tOut, Monad m) => ResidHandler tIn tOut -> Iter tIn m tOut -> Inum tIn tOut m a -- | Bracket an Inum with a start and end function, which can be -- used to acquire and release a resource, must like the IO monad's -- bracket function. For example: -- --
-- enumFile :: (MonadIO m, ChunkData t, LL.ListLikeIO t e) => -- FilePath -> Onum t m a -- enumFile path = inumBracket (liftIO $ openBinaryFile path ReadMode) -- (liftIO . hClose) -- enumHandle --inumBracket :: (ChunkData tIn, Monad m) => Iter tIn m b -> (b -> Iter tIn m c) -> (b -> Inum tIn tOut m a) -> Inum tIn tOut m a -- | pullupResid (a, b) = (mappend a b, mempty). See -- ResidHandler. pullupResid :: ChunkData t => (t, t) -> (t, t) -- | Reject all control requests. noCtl :: Monad m1 => CtlHandler m1 t m a -- | Pass all control requests through to the enclosing Iter monad. -- The ResidHandler argument says how to adjust residual data, in -- case some enclosing CtlHandler decides to flush pending input -- data, it is advisable to un-translate any data in the output type -- tOut back to the input type tIn. passCtl :: Monad mIn => ResidHandler tIn tOut -> CtlHandler (Iter tIn mIn) tOut m a -- | Create a CtlHandler given a function of a particular control -- argument type and a fallback CtlHandler to run if the argument -- type does not match. consCtl is used to chain handlers, with -- the rightmost handler being either noCtl or passCtl. -- -- For example, to create a control handler that implements seek on -- SeekC requests, returns the size of the file on -- SizeC requests, and passes everything else out to -- the enclosing enumerator (if any), you could use the following: -- --
-- fileCtl :: (ChunkData t, MonadIO m) => Handle -> CtlHandler (Iter () m) t m a -- fileCtl h = (mkFlushCtl $ (SeekC mode pos) -> liftIO (hSeek h mode pos)) -- `consCtl` (mkCtl $ SizeC -> liftIO (hFileSize h)) -- `consCtl` passCtl id ---- -- Has fixity: -- --
-- infixr 9 `consCtl` --consCtl :: (CtlCmd carg cres, ChunkData tIn, Monad mIn) => (carg -> (cres -> Iter t m a) -> Chunk t -> Iter tIn mIn (IterR t m a)) -> CtlHandler (Iter tIn mIn) t m a -> CtlHandler (Iter tIn mIn) t m a -- | Make a control function suitable for use as the first argument to -- consCtl. mkCtl :: (CtlCmd carg cres, Monad m1) => (carg -> Iter t1 m1 cres) -> carg -> (cres -> Iter t m a) -> Chunk t -> Iter t1 m1 (IterR t m a) -- | Like mkCtl, except that it flushes all input and clears the EOF -- flag in both Iter monads after executing the control function. mkFlushCtl :: (CtlCmd carg cres, Monad mIn, ChunkData tIn, ChunkData t) => (carg -> Iter tIn mIn cres) -> carg -> (cres -> Iter t m a) -> Chunk t -> Iter tIn mIn (IterR t m a) -- | Like runIterMC, but only for IterM--may return -- IterC. runIterM :: (Monad m, MonadTrans mt, Monad (mt m)) => Iter t m a -> Chunk t -> mt m (IterR t m a) -- | Run an Iter just like runIter, but then keep stepping -- the result for as long as it is in the IterM or IterC -- state (using the supplied CtlHandler for IterC states). -- Inums should generally use this function or runIterM in -- preference to runIter, as it is convenient if Inums -- avoid ever returning IterRs in the IterM state. runIterMC :: Monad m => CtlHandler (Iter tIn m) tOut m a -> Iter tOut m a -> Chunk tOut -> Iter tIn m (IterR tOut m a) -- | Takes an Inum that might return IterRs in the -- IterM state (which is considered impolite--see -- runIterMC) and transforms it into an Inum that never -- returns IterRs in the IterM state. runInum :: (ChunkData tIn, Monad m) => Inum tIn tOut m a -> Inum tIn tOut m a -- | inumNop passes all data through to the underlying -- Iter. It acts as a no-op when fused to other Inums with -- |. or when fused to Iters with .|. -- -- inumNop is particularly useful for conditionally fusing -- Inums together. Even though most Inums are polymorphic -- in the return type, this library does not use the Rank2Types -- extension, which means any given Inum must have a specific -- return type. Here is an example of incorrect code: -- --
-- let enum = if debug then base_enum |. inumStderr else base_enum -- Error ---- -- This doesn't work because base_enum cannot have the same type -- as (base_enum |. inumStderr). Instead, you can use the -- following: -- --
-- let enum = base_enum |. if debug then inumStderr else inumNop --inumNop :: (ChunkData t, Monad m) => Inum t t m a -- | inumNull feeds empty data to the underlying Iter. It -- pretty much acts as a no-op when concatenated to other Inums -- with cat or lcat. -- -- There may be cases where inumNull is required to avoid -- deadlock. In an expression such as enum |$ iter, if -- enum immediately blocks waiting for some event, and -- iter immediately starts out triggering that event before -- reading any input, then to break the deadlock you can re-write the -- code as cat inumNull enum |$ iter. inumNull :: (ChunkData tOut, Monad m) => Inum tIn tOut m a -- | Feed pure data to an Iter. inumPure :: Monad m => tOut -> Inum tIn tOut m a -- | Type-restricted version of inumPure. enumPure :: Monad m => tOut -> Onum tOut m a -- | Repeat an Inum until the input receives an EOF condition, the -- Iter no longer requires input, or the Iter is in an -- unhandled IterC state (which presumably will continue to be -- unhandled by the same Inum, so no point in executing it again). inumRepeat :: (ChunkData tIn, Monad m) => Inum tIn tOut m a -> Inum tIn tOut m a -- | A monad in which to define the actions of an Inum tIn tOut -- m a. Note InumM tIn tOut m a is a Monad of kind -- * -> *, where a is the (almost always parametric) -- return type of the Inum. A fifth type argument is required for -- monadic computations of kind *, e.g.: -- --
-- seven :: InumM tIn tOut m a Int -- seven = return 7 ---- -- Another important thing to note about the InumM monad, as -- described in the documentation for mkInumM, is that you must -- call lift twice to execute actions in monad -- m, and you must use the liftI function to execute -- actions in monad Iter t m a. type InumM tIn tOut m a = Iter tIn (IterStateT (InumState tIn tOut m a) m) -- | Build an Inum out of an InumM computation. If you run -- mkInumM inside the Iter tIn m monad (i.e., to -- create an enumerator of type Inum tIn tOut m a), then -- the InumM computation will be in a Monad of type -- Iter t tm where tm is a transformed version -- of m. This has the following two consequences: -- --
-- irepeat $ liftIO (LL.hGet h defaultChunkSize) >>= ifeed1 --ifeed1 :: (ChunkData tIn, ChunkData tOut, Monad m) => tOut -> InumM tIn tOut m a Bool -- | Apply another Inum to the target Iter from within the -- InumM monad. As with ifeed, returns True -- when the Iter is finished. -- -- Note that the applied Inum must handle all control requests. -- (In other words, ones it passes on are not caught by whatever handler -- is installed by setCtlHandler, but if the Inum returns -- the IterR in the IterC state, as inumPure does, -- then requests will be handled.) ipipe :: (ChunkData tIn, ChunkData tOut, Monad m) => Inum tIn tOut m a -> InumM tIn tOut m a Bool -- | Apply an Onum (or Inum of an arbitrary, unused input -- type) to the Iter from within the InumM monad. As with -- ifeed, returns True when the Iter is -- finished. irun :: (ChunkData tAny, ChunkData tIn, ChunkData tOut, Monad m) => Inum tAny tOut m a -> InumM tIn tOut m a Bool -- | Repeats an action until the Iter is done or an EOF error is -- thrown. (Also stops if a different kind of exception is thrown, in -- which case the exception propagates further and may cause the -- Inum to fail.) irepeat sets both the AutoEOF -- and AutoDone flags to True. irepeat :: (ChunkData tIn, Monad m) => InumM tIn tOut m a b -> InumM tIn tOut m a () -- | If the target Iter being fed by the Inum is no longer -- active (i.e., if it is in the Done state or in an error state), -- this funciton pops the residual data out of the Iter and -- returns it. If the target is in any other state, returns -- mempty. ipopresid :: (ChunkData tIn, ChunkData tOut, Monad m) => InumM tIn tOut m a tOut -- | Immediately perform a successful exit from an InumM monad, -- terminating the Inum and returning the current state of the -- target Iter. Can be used to end an irepeat loop. (Use -- throwI ... for an unsuccessful exit.) idone :: (ChunkData tIn, Monad m) => InumM tIn tOut m a b -- | This module contains miscellaneous functions plus a few pieces of -- functionality that are missing from the standard Haskell libraries. module Data.IterIO.Extra -- | Create a loopback (Iter, Onum) pair. The -- iteratee and enumerator can be used in different threads. Any data fed -- into the Iter will in turn be fed by the Onum into -- whatever Iter it is given. This is useful for testing a -- protocol implementation against itself. iterLoop :: (MonadIO m, ChunkData t, Show t) => m (Iter t m (), Onum t m a) -- | Returns an Iter that always returns itself until a result is -- produced. You can fuse inumSplit to an Iter to produce -- an Iter that can safely be fed (e.g., with enumPure) -- from multiple threads. inumSplit :: (MonadIO m, ChunkData t) => Inum t t m a -- | SendRecvString is the class of string-like objects that can -- be used with datagram sockets. class Show t => SendRecvString t genRecv :: SendRecvString t => Socket -> Int -> IO t genSend :: SendRecvString t => Socket -> t -> IO () genRecvFrom :: SendRecvString t => Socket -> Int -> IO (t, SockAddr) genSendTo :: SendRecvString t => Socket -> t -> SockAddr -> IO () -- | Flushes a file handle and calls the shutdown system call so as -- to write an EOF to a socket while still being able to read from it. -- This is very important when the same file handle is being used to to -- read data in an Onum and to write data in an Iter. -- Proper protocol functioning may require the Iter to send an EOF -- (e.g., a TCP FIN segment), but the Onum may still be reading -- from the socket in a different thread. hShutdown :: Handle -> CInt -> IO Int -- | For debugging, print a tag along with the current residual input. Not -- referentially transparent. traceInput :: (ChunkData t, Monad m) => String -> Iter t m () -- | For debugging. Print the current thread ID and a message. Not -- referentially transparent. traceI :: (ChunkData t, Monad m) => String -> Iter t m () instance SendRecvString ByteString instance SendRecvString ByteString instance SendRecvString [Char] -- | This module contains basic iteratees and enumerators for working with -- strings, ListLike objects, file handles, and stream and -- datagram sockets. module Data.IterIO.ListLike -- | An Iteratee that puts data to a consumer function, then calls an eof -- function. For instance, handleI could be defined as: -- --
-- handleI :: (MonadIO m) => Handle -> Iter ByteString m () -- handleI h = putI (liftIO . hPut h) (liftIO $ hShutdown h 1) --putI :: (ChunkData t, Monad m) => (t -> Iter t m a) -> Iter t m b -> Iter t m () -- | Send datagrams using a supplied function. The datagrams are fed as a -- list of packets, where each element of the list should be a separate -- datagram. For example, to create an Iter from a connected UDP -- socket: -- --
-- udpI :: (SendRecvString s, MonadIO m) => Socket -> Iter s m () -- udpI sock = sendI $ liftIO . genSend sock --sendI :: (Show t, Monad m) => (t -> Iter [t] m a) -> Iter [t] m () -- | Return the first element when the Iteratee data type is a list. headLI :: (Show a, Monad m) => Iter [a] m a -- | Return Just the first element when the Iteratee data type is a -- list, or Nothing on EOF. safeHeadLI :: (Show a, Monad m) => Iter [a] m (Maybe a) -- | Like headLI, but works for any ListLike data type. headI :: (ChunkData t, ListLike t e, Monad m) => Iter t m e -- | Like safeHeadLI, but works for any ListLike data type. safeHeadI :: (ChunkData t, ListLike t e, Monad m) => Iter t m (Maybe e) -- | Return a line delimited by \r, \n, or \r\n. lineI :: (Monad m, ChunkData t, ListLike t e, Eq t, Enum e, Eq e) => Iter t m t -- | Like lineI, but returns Nothing on EOF. safeLineI :: (ChunkData t, Monad m, ListLike t e, Eq t, Enum e, Eq e) => Iter t m (Maybe t) -- | Return ListLike data that is at most the number of elements -- specified by the first argument, and at least one element (as long as -- a positive number is requested). Throws an exception if a positive -- number of items is requested and an EOF is encountered. dataMaxI :: (ChunkData t, ListLike t e, Monad m) => Int -> Iter t m t -- | Return ListLike data that is at most the number of elements -- specified by the first argument, and at least one element unless EOF -- is encountered or 0 elements are requested, in which case -- mempty is returned. data0MaxI :: (ChunkData t, ListLike t e, Monad m) => Int -> Iter t m t -- | Return the next len elements of a ListLike data -- stream, unless an EOF is encountered, in which case fewer may be -- returned. Note the difference from data0MaxI: takeI -- n will keep reading input until it has accumulated n -- elements or seen an EOF, then return the data; data0MaxI -- n will keep reading only until it has received any non-empty -- amount of data, even if the amount received is less than n -- elements and there is no EOF. takeI :: (ChunkData t, ListLike t e, Monad m) => Int -> Iter t m t -- | Puts strings (or ListLikeIO data) to a file Handle, then -- writes an EOF to the handle. -- -- Note that this does not put the handle into binary mode. To do this, -- you may need to call hSetBinaryMode h True on -- the handle before using it with handleI. Otherwise, Haskell -- by default will treat the data as UTF-8. (On the other hand, if the -- Handle corresponds to a socket and the socket is being read in -- another thread, calling hSetBinaryMode can cause deadlock, so -- in this case it is better to have the thread handling reads call -- hSetBinaryMode.) -- -- Also note that Haskell by default buffers data written to -- Handles. For many network protocols this is a problem. Don't -- forget to call hSetBuffering h NoBuffering -- before passing a handle to handleI. handleI :: (MonadIO m, ChunkData t, ListLikeIO t e) => Handle -> Iter t m () -- | Sends a list of packets to a datagram socket. sockDgramI :: (MonadIO m, SendRecvString t) => Socket -> Maybe SockAddr -> Iter [t] m () -- | Sends output to a stream socket. Calls shutdown (e.g., to send a TCP -- FIN packet) upon receiving EOF. sockStreamI :: (ChunkData t, SendRecvString t, MonadIO m) => Socket -> Iter t m () -- | An Iter that uses hPutStr to write all output to -- stdout. stdoutI :: (ListLikeIO t e, ChunkData t, MonadIO m) => Iter t m () -- | A mode that determines the effect of hSeek hdl mode -- i. data SeekMode :: * -- | the position of hdl is set to i. AbsoluteSeek :: SeekMode -- | the position of hdl is set to offset i from the -- current position. RelativeSeek :: SeekMode -- | the position of hdl is set to offset i from the end -- of the file. SeekFromEnd :: SeekMode -- | A control command (issued with ctlI SizeC) requesting -- the size of the current file being enumerated. data SizeC SizeC :: SizeC -- | A control command for seeking within a file, when a file is being -- enumerated. Flushes the residual input data. data SeekC SeekC :: !SeekMode -> !Integer -> SeekC -- | A control command for determining the current offset within a file. -- Note that this subtracts the size of the residual input data from the -- offset in the file. Thus, it will only be accurate when all left-over -- input data is from the current file. data TellC TellC :: TellC -- | A handler function for the SizeC, SeekC, and -- TellC control requests. fileCtl is used internally by -- enumFile and enumHandle, and is exposed for similar -- enumerators to use. fileCtl :: (ChunkData t, ListLike t e, MonadIO m) => Handle -> CtlHandler (Iter () m) t m a -- | A control request that returns the Socket from an enclosing -- socket enumerator. data GetSocketC GetSocketC :: GetSocketC -- | A handler for the GetSocketC control request. socketCtl :: (ChunkData t, MonadIO m) => Socket -> CtlHandler (Iter () m) t m a -- | Read datagrams (of up to 64KiB in size) from a socket and feed a list -- of strings (one for each datagram) into an Iteratee. enumDgram :: (MonadIO m, SendRecvString t) => Socket -> Onum [t] m a -- | Read datagrams from a socket and feed a list of (Bytestring, SockAddr) -- pairs (one for each datagram) into an Iteratee. enumDgramFrom :: (MonadIO m, SendRecvString t) => Socket -> Onum [(t, SockAddr)] m a -- | Read data from a stream (e.g., TCP) socket. enumStream :: (MonadIO m, ChunkData t, SendRecvString t) => Socket -> Onum t m a -- | Puts a handle into binary mode with hSetBinaryMode, then -- enumerates data read from the handle to feed an Iter with any -- ListLikeIO input type. enumHandle :: (MonadIO m, ChunkData t, ListLikeIO t e) => Handle -> Onum t m a -- | A variant of enumHandle type restricted to input in the Lazy -- ByteString format. enumHandle' :: MonadIO m => Handle -> Onum ByteString m a -- | Feeds an Iter with data from a file handle, using any input -- type in the ListLikeIO class. Note that -- enumNonBinHandle uses the handle as is, unlike -- enumHandle, and so can be used if you want to read the data in -- non-binary form. enumNonBinHandle :: (MonadIO m, ChunkData t, ListLikeIO t e) => Handle -> Onum t m a -- | Enumerate the contents of a file for an Iter taking input in -- any ListLikeIO type. Note that the file is opened with -- openBinaryFile to ensure binary mode. enumFile :: (MonadIO m, ChunkData t, ListLikeIO t e) => FilePath -> Onum t m a -- | Enumerate the contents of a file as a series of lazy -- ByteStrings. (This is a type-restricted version of -- enumFile.) enumFile' :: MonadIO m => FilePath -> Onum ByteString m a -- | Enumerate standard input. enumStdin :: (MonadIO m, ChunkData t, ListLikeIO t e) => Onum t m a -- | Feed up to some number of list elements (bytes in the case of -- ByteStrings) to an Iter, or feed fewer if the -- Iter returns or an EOF is encountered. The formulation -- inumMax n .| iter can be used to prevent iter -- from consuming unbounded amounts of input. inumMax :: (ChunkData t, ListLike t e, Monad m) => Int -> Inum t t m a -- | Feed exactly some number of bytes to an Iter. Throws an error -- if that many bytes are not available. inumTakeExact :: (ChunkData t, ListLike t e, Monad m) => Int -> Inum t t m a -- | This inner enumerator is like inumNop in that it passes -- unmodified Chunks straight through to an iteratee. However, it -- also logs the Chunks to a file (which can optionally be -- truncated or appended to, based on the second argument). inumLog :: (MonadIO m, ChunkData t, ListLikeIO t e) => FilePath -> Bool -> Inum t t m a -- | Like inumLog, but takes a writeable file handle rather than a -- file name. Does not close the handle when done. inumhLog :: (MonadIO m, ChunkData t, ListLikeIO t e) => Handle -> Inum t t m a -- | Log a copy of everything to standard error. (inumStderr = -- inumhLog stderr) inumStderr :: (MonadIO m, ChunkData t, ListLikeIO t e) => Inum t t m a -- | An Inum that converts input in the lazy ByteString -- format to strict ByteStrings. inumLtoS :: Monad m => Inum ByteString ByteString m a -- | The dual of inumLtoS--converts input from strict -- ByteStrings to lazy ByteStrings. inumStoL :: Monad m => Inum ByteString ByteString m a -- | Add a finalizer to run when an Iter has received an EOF and an -- Inum has finished. This works regardless of the order in which -- the two events happen. pairFinalizer :: (ChunkData t, ChunkData t1, ChunkData t2, MonadIO m, MonadIO m1) => Iter t m a -> Inum t1 t2 m1 b -> IO () -> IO (Iter t m a, Inum t1 t2 m1 b) -- | "Iterizes" a file Handle by turning into an Onum (for -- reading) and an Iter (for writing). Uses pairFinalizer -- to hClose the Handle when both the Iter and -- Onum are finished. Puts the handle into binary mode, but does -- not change the buffering. iterHandle :: (ListLikeIO t e, ChunkData t, MonadIO m) => Handle -> IO (Iter t m (), Onum t m a) -- | "Iterizes" a stream Socket by turning into an Onum (for -- reading) and an Iter (for writing). Uses pairFinalizer -- to sClose the Socket when both the Iter and -- Onum are finished. iterStream :: (SendRecvString t, ChunkData t, MonadIO m) => Socket -> IO (Iter t m (), Onum t m a) instance Typeable SizeC instance Typeable SeekC instance Typeable TellC instance Typeable GetSocketC instance CtlCmd GetSocketC Socket instance CtlCmd TellC Integer instance CtlCmd SeekC () instance CtlCmd SizeC Integer -- | This module contains functions to help parsing input from within -- Iters. Many of the operators are either imported from -- Data.Applicative or inspired by Text.Parsec. module Data.IterIO.Parse -- | An infix synonym for multiParse that allows LL(*) parsing of -- alternatives by executing both Iteratees on input chunks as they -- arrive. This is similar to the <|> method of the -- Alternative class in Control.Applicative, but -- the Alternative operator has left fixity, while for -- efficiency this one has: -- --
-- infixr 3 <|> --(<|>) :: (ChunkData t, Monad m) => Iter t m a -> Iter t m a -> Iter t m a -- | An infix synonym for ifNoParse that allows LL(*) parsing of -- alternatives by keeping a copy of input data consumed by the first -- Iteratee so as to backtrack and execute the second Iteratee if the -- first one fails. Returns a function that takes a continuation for the -- first Iter, should it succeed. The code: -- --
-- iter1 \/ iter2 $ \iter1Result -> doSomethingWith iter1Result ---- -- Executes iter1 (saving a copy of the input for backtracking). -- If iter1 fails with an exception of class -- IterNoParse, then the input is re-wound and fed to -- iter2. On the other hand, if iter1 succeeds and -- returns iter1Result, then the saved input is discarded (as -- iter2 will not need to be run) and the result of -- iter1 is fed to function doSomethingWith. -- -- For example, to build up a list of results of executing iter, -- one could implement a type-restricted version of many as -- follows: -- --
-- myMany :: (ChunkData t, Monad m) => Iter t m a -> Iter t m [a] -- myMany iter = iter \/ return [] $ \r -> fmap ((:) r) (myMany iter) ---- -- In other words, myMany tries running iter. If -- iter fails, then myMany returns the empty list. If -- iter succeeds, its result r is added to the head of -- the list returned by calling myMany recursively. This idiom -- of partially applying a binary funciton to a result and then applying -- the resulting function to an Iter via fmap is so common -- that there is an infix operator for it, >$>. -- Thus, the above code can be written: -- --
-- myMany iter = iter \/ return [] $ (:) >$> myMany iter ---- -- Of course, using fmap is not the most efficient way to -- implement myMany. If you are going to use this pattern for -- something performance critical, you should use an accumulator rather -- than build up long chains of fmaps. A faster implementation -- would be: -- --
-- myMany iter = loop id -- where loop ac = iter \/ return (acc []) $ a -> loop (acc . (a :)) ---- -- \/ has fixity: -- --
-- infix 2 \/ --(\/) :: (ChunkData t, Monad m) => Iter t m a -> Iter t m b -> (a -> Iter t m b) -> Iter t m b -- | Defined as orEmpty = (\/ return mempty), and -- useful when parse failures should just return an empty Monoid. -- For example, a type-restricted many can be implemented as: -- --
-- myMany :: (ChunkData t, Monad m) => Iter t m a -> Iter t m [a] -- myMany iter = iter `orEmpty` (:) >$> myMany iter ---- -- Has fixity: -- --
-- infixr 3 `orEmpty` --orEmpty :: (ChunkData t, Monad m, Monoid b) => Iter t m a -> (a -> Iter t m b) -> Iter t m b -- | iter <?> token replaces any kind of parse failure in -- iter with an exception equivalent to calling -- expectedI prefix token where prefix is a -- prefix of the input that was fed to iter and caused it to -- fail. -- -- Has fixity: -- --
-- infix 0 <?> --(>) :: (ChunkData t, Monad m) => Iter t m a -> String -> Iter t m a -- | Throw an Iter exception that describes expected input not -- found. expectedI :: ChunkData t => String -> String -> Iter t m a -- | Takes an Iter returning a ListLike type, executes the -- Iter once, and throws a parse error if the returned value is -- null. (Note that this is quite different from the -- some method of the Alternative -- class in Control.Applicative, which executes a computation one -- or more times. This library does not use -- Alternative because Alternative's -- <|> operator has left instead of right fixity.) someI :: (ChunkData t, Monad m, ListLike a e) => Iter t m a -> Iter t m a -- | Repeatedly invoke an Iter and right-fold a function over the -- results. foldrI :: (ChunkData t, Monad m) => (a -> b -> b) -> b -> Iter t m a -> Iter t m b -- | A variant of foldrI that requires the Iter to succeed at -- least once. foldr1I :: (ChunkData t, Monad m) => (a -> b -> b) -> b -> Iter t m a -> Iter t m b -- | A variant of foldrI that requires the Iter to succeed at -- least a minimum number of items and stops parsing after executing the -- Iter some maximum number of times. foldrMinMaxI :: (ChunkData t, Monad m) => Int -> Int -> (a -> b -> b) -> b -> Iter t m a -> Iter t m b -- | Strict left fold over an Iter (until it throws an -- IterNoParse exception). foldlI f z iter is sort of -- equivalent to: -- --
-- ... (f <$> (f <$> (f z <$> iter) <*> iter) <*> iter) ... --foldlI :: (ChunkData t, Monad m) => (b -> a -> b) -> b -> Iter t m a -> Iter t m b -- | A version of foldlI that fails if the Iter argument does -- not succeed at least once. foldl1I :: (ChunkData t, Monad m) => (b -> a -> b) -> b -> Iter t m a -> Iter t m b -- | foldMI is a left fold in which the folding function can -- execute monadic actions. Essentially foldMI is to -- foldlI as foldM is to foldl' in the -- standard libraries. foldMI :: (ChunkData t, Monad m) => (b -> a -> Iter t m b) -> b -> Iter t m a -> Iter t m b -- | A variant of foldMI that requires the Iter to succeed at -- least once. foldM1I :: (ChunkData t, Monad m) => (b -> a -> Iter t m b) -> b -> Iter t m a -> Iter t m b -- | Discard the result of executing an Iteratee once. Throws an error if -- the Iteratee fails. (Like skip x = x >> return ().) skipI :: Applicative f => f a -> f () -- | Execute an iteratee. Discard the result if it succeeds. Rewind the -- input and suppress the error if it fails. optionalI :: (ChunkData t, Monad m) => Iter t m a -> Iter t m () -- | Ensures the next input element satisfies a predicate or throws a parse -- error. Does not consume any input. ensureI :: (ChunkData t, ListLike t e, Monad m) => (e -> Bool) -> Iter t m () -- | A variant of the standard library ord function, but that -- translates a Char into any Enum type, not just -- Int. Particularly useful for Iters that must work with -- both Strings (which consist of Chars) and ASCII -- ByteStrings (which consist of -- Word8s). For example, to skip one or more space or -- TAB characters, you can use: -- --
-- skipSpace :: (ListLike t e, ChunkData t, Eq e, Enum e, Monad m) => -- Iter t m () -- skipSpace = skipWhile1I (\c -> c == eord ' ' || c == eord '\t') --eord :: Enum e => Char -> e -- | Skip all input elements encountered until an element is found that -- does not match the specified predicate. skipWhileI :: (ChunkData t, ListLike t e, Monad m) => (e -> Bool) -> Iter t m () -- | Like skipWhileI, but fails if at least one element does not -- satisfy the predicate. skipWhile1I :: (ChunkData t, ListLike t e, Monad m) => (e -> Bool) -> Iter t m () -- | Return all input elements up to the first one that does not match the -- specified predicate. whileI :: (ChunkData t, ListLike t e, Monad m) => (e -> Bool) -> Iter t m t -- | Like whileI, but fails if at least one element does not satisfy -- the predicate. while1I :: (ChunkData t, ListLike t e, Monad m) => (e -> Bool) -> Iter t m t -- | A variant of whileI with a maximum number matches. whileMaxI :: (ChunkData t, ListLike t e, Monad m) => Int -> (e -> Bool) -> Iter t m t -- | A variant of whileI with a minimum and maximum number matches. whileMinMaxI :: (ChunkData t, ListLike t e, Monad m) => Int -> Int -> (e -> Bool) -> Iter t m t -- | Repeatedly execute an Iter returning a Monoid and -- mappend all the results in a right fold. concatI :: (ChunkData t, Monoid s, Monad m) => Iter t m s -> Iter t m s -- | Like concatI, but fails if the Iter doesn't return at -- least once. concat1I :: (ChunkData t, Monoid s, Monad m) => Iter t m s -> Iter t m s -- | A version of concatI that takes a minimum and maximum number of -- items to parse. concatMinMaxI :: (ChunkData t, Monoid s, Monad m) => Int -> Int -> Iter t m s -> Iter t m s -- | This Iter parses a StringLike argument. It does not -- consume any Iteratee input. The only reason it is an Iteratee is so -- that it can throw an Iteratee parse error should it fail to parse the -- argument string (or should the argument yield an ambiguous parse). readI :: (ChunkData t, Monad m, StringLike s, Read a) => s -> Iter t m a -- | Ensures the input is at the end-of-file marker, or else throws an -- exception. eofI :: (ChunkData t, Monad m, Show t) => Iter t m () -- | An infix synonym for fmap. (<$>) :: Functor f => (a -> b) -> f a -> f b -- | Replace all locations in the input with the same value. The default -- definition is fmap . const, but this may be -- overridden with a more efficient version. (<$) :: Functor f => forall a b. a -> f b -> f a -- | fa $> b = b <$ fa -- replaces the output value of a -- functor with some pure value. Has the same fixity as <$> -- and <$, namely: -- --
-- infixl 4 $> --($>) :: Functor f => f a -> b -> f b -- | (f >$> a) t is equivalent to f t <$> -- a (where <$> is and infix alias for fmap). -- Particularly useful with infix combinators such as \/ and -- `orEmpty` when chaining parse actions. See examples at -- \/ and orEmpty. Note fmap is not always the most -- efficient solution (see an example in the description of \/). -- -- Has fixity: -- --
-- infixl 3 >$> --(>$>) :: Functor f => (t -> a -> b) -> f a -> t -> f b -- | A functor with application. -- -- Instances should satisfy the following laws: -- --
-- fmap f x = pure f <*> x ---- -- If f is also a Monad, define pure = -- return and (<*>) = ap. -- -- Minimal complete definition: pure and <*>. class Functor f => Applicative f :: (* -> *) pure :: Applicative f => a -> f a (<*>) :: Applicative f => f (a -> b) -> f a -> f b (*>) :: Applicative f => f a -> f b -> f b (<*) :: Applicative f => f a -> f b -> f a -- | A variant of <*> with the arguments reversed. (<**>) :: Applicative f => f a -> f (a -> b) -> f b -- | mappend the result of two Applicative types returning -- Monoid types (<++> = liftA2 -- mappend). Has the same fixity as ++, namely: -- --
-- infixr 5 <++> --(<++>) :: (Applicative f, Monoid t) => f t -> f t -> f t -- | cons an Applicative type onto an an Applicative -- ListLike type (<:> = liftA2 cons). -- Has the same fixity as :, namely: -- --
-- infixr 5 <:> --(<:>) :: (ListLike t e, Applicative f) => f e -> f t -> f t -- | nil = pure mempty--An empty Monoid -- injected into an Applicative type. nil :: (Applicative f, Monoid t) => f t -- | Run an Iter zero or more times (until it fails) and return a -- list-like container of the results. many :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m f -- | Repeatedly run an Iter until it fails and discard all the -- results. skipMany :: (ChunkData t, Monad m) => Iter t m a -> Iter t m () -- | Parses a sequence of the form Item1 Separator Item2 Separator ... -- Separator ItemN and returns the list -- [Item1, Item2, ..., -- ItemN] or a ListLike equivalent. sepBy :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m b -> Iter t m f -- | Like sepBy, but expects a separator after the final item. In -- other words, parses a sequence of the form Item1 Separator Item2 -- Separator ... Separator ItemN Separator and returns the list -- [Item1, Item2, ..., -- ItemN] or a ListLike equivalent. endBy :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m b -> Iter t m f -- | Accepts items that would be parsed by either sepBy or -- endBy. Essentially a version of endBy in which the final -- separator is optional. sepEndBy :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m b -> Iter t m f -- | Run an Iter one or more times (until it fails) and return a -- list-like container of the results. many1 :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m f -- | A variant of skipMany that throws a parse error if the -- Iter does not succeed at least once. skipMany1 :: (ChunkData t, Monad m) => Iter t m a -> Iter t m () -- | A variant of sepBy that throws a parse error if it cannot -- return at least one item. sepBy1 :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m b -> Iter t m f -- | A variant of endBy that throws a parse error if it cannot -- return at least one item. endBy1 :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m b -> Iter t m f -- | A variant of sepEndBy that throws a parse error if it cannot -- return at least one item. sepEndBy1 :: (ChunkData t, ListLike f a, Monad m) => Iter t m a -> Iter t m b -> Iter t m f -- | Read the next input element if it satisfies some predicate. Otherwise -- throw an error. satisfy :: (ChunkData t, ListLike t e, Enum e, Monad m) => (e -> Bool) -> Iter t m e -- | Read input that exactly matches a character. char :: (ChunkData t, ListLike t e, Eq e, Enum e, Monad m) => Char -> Iter t m e -- | Read input that exactly matches some target. match :: (ChunkData t, ListLike t e, Eq e, Monad m) => t -> Iter t m t -- | Read input that exactly matches a string. string :: (ChunkData t, ListLike t e, StringLike t, Eq e, Monad m) => String -> Iter t m t -- | Read input that matches a string up to case. stringCase :: (ChunkData t, ListLike t e, Enum e, Eq e, Monad m) => String -> Iter t m t module Data.IterIO.Search -- | Feeds input to an Iteratee until some boundary string is found. The -- boundary string is neither consumed nor passed through to the target -- Iter. (Thus, if the input is at end-of-file after -- inumStopString returns, it means the boundary string was never -- encountered.) inumStopString :: Monad m => ByteString -> Inum ByteString ByteString m a -- | Reads input until it can uniquely determine the longest key in a -- Map that is a prefix of the input. Consumes the input that -- matches the key, and returns the corresponding value in the -- Map, along with the residual input that follows the key. mapI :: (ChunkData t, ListLike t e, Ord t, Eq e, Monad m) => Map t a -> Iter t m a -- | mapLI is a variant of mapI that takes a list of -- (key, value) pairs instead of a Map. mapLI = -- mapI . fromList. mapLI :: (ChunkData t, ListLike t e, Ord t, Eq e, Monad m) => [(t, a)] -> Iter t m a module Data.IterIO.SSL -- | A wrapper around the type SSL to make it an instance of the -- Typeable class. newtype SslConnection SslConnection :: SSL -> SslConnection unSslConnection :: SslConnection -> SSL -- | Control request to fetch the SSL object associated with an -- enumerator. data SslC SslC :: SslC -- | Simple OpenSSL Onum. enumSsl :: MonadIO m => SSL -> Onum ByteString m a -- | Simple OpenSSL Iter. Does a uni-directional SSL shutdown when -- it receives a Chunk with the EOF bit True. sslI :: MonadIO m => SSL -> Iter ByteString m () -- | Turn a socket into an Iter and Onum that use OpenSSL to -- write to and read from the socket, respectively. Does an SSL -- bi-directional shutdown and closes the socket when both a) the enum -- completes and b) the iter has received an EOF chunk. -- -- If the SSL handshake fails, then iterSSL closes the socket -- before throwing an exception. -- -- This funciton must only be invoked from within a call to -- withOpenSSL. iterSSL :: MonadIO m => SSLContext -> Socket -> Bool -> IO (Iter ByteString m (), Onum ByteString m a) -- | Simplest possible SSL context, loads cert and unencrypted private key -- from a single file. simpleContext :: FilePath -> IO SSLContext -- | Quick and dirty funciton to generate a self signed certificate for -- testing and stick it in a file. E.g.: -- --
-- genSelfSigned "testkey.pem" "localhost" --genSelfSigned :: FilePath -> String -> IO () instance Typeable SslConnection instance Typeable SslC instance CtlCmd SslC SslConnection module Data.IterIO.Zlib -- | State used by inumZState, the most generic zlib Inum. -- Create the state using deflateInit2 or inflateInit2. data ZState -- | Create a ZState for compression. See the description of -- deflateInit2 in the zlib.h C header file for a more detailed -- description of the arguments. Note in particular that the value of -- windowBits determines the encapsulation format of the -- compressed data: -- --
-- do let docontrol _ field = do -- liftIO $ putStrLn $ -- "The value of " ++ (S8.unpack $ ffName field) ++ " is:" -- stdoutI -- Send form value to standard output -- liftIO $ putStrLn "\n" -- foldForm req docontrol () ---- -- Or to produce a list of (field, value) pairs, you can say something -- like: -- --
-- do let docontrol acc field = do -- val <- pureI -- return $ (ffName field, val) : acc -- foldForm req docontrol [] ---- -- Note that for POSTed forms of enctype -- application/x-www-form-urlencoded, foldForm will -- read to the end of its input. Thus, it is important to ensure -- foldForm is called from within an inumHttpBody -- enumerator (which is guaranteed by inumHttpServer). foldForm :: Monad m => HttpReq -> (a -> FormField -> Iter ByteString m a) -> a -> Iter ByteString m a -- | HTTP status code and text description of response, for the first line -- of an HTTP response message. A bunch of pre-defined statuses from RFC -- 2616 are supplied under the names stat200, stat404, -- stat500, etc. data HttpStatus HttpStatus :: !Int -> !ByteString -> HttpStatus stat100 :: HttpStatus stat200 :: HttpStatus stat301 :: HttpStatus stat302 :: HttpStatus stat303 :: HttpStatus stat304 :: HttpStatus stat400 :: HttpStatus stat401 :: HttpStatus stat403 :: HttpStatus stat404 :: HttpStatus stat405 :: HttpStatus stat500 :: HttpStatus stat501 :: HttpStatus -- | A data structure describing an HTTP response message to be sent, -- parameterized by the Monad in which the response will be written to -- the network. data HttpResp m HttpResp :: !HttpStatus -> ![ByteString] -> !Bool -> !Onum ByteString m (IterR ByteString m ()) -> HttpResp m -- | The response status. respStatus :: HttpResp m -> !HttpStatus -- | Headers to send back respHeaders :: HttpResp m -> ![ByteString] -- | True if the message body should be passed through inumToChunks -- and a "Transfer-Encoding: chunked" header should be added. -- Generally this should be True unless you have added a -- Content-Length header, manually set up chunk encoding by -- fusing it in respBody, or are not returning a message body with -- the reply. respChunk :: HttpResp m -> !Bool -- | Onum producing the message body. Use inumNull (which is -- an empty Inum) to produce an empty body for responses that do -- not contain a body. respBody :: HttpResp m -> !Onum ByteString m (IterR ByteString m ()) -- | An empty HTTP response, to which you must add headers and possibly a -- message body. defaultHttpResp :: Monad m => HttpResp m -- | Generate an HttpResp without a body. mkHttpHead :: Monad m => HttpStatus -> HttpResp m -- | Generate an HttpResp with a body of type text/html. mkHtmlResp :: Monad m => HttpStatus -> ByteString -> HttpResp m -- | Make an HttpResp of an arbitrary content-type based on a pure -- lazy ByteString. Since the result is pure, this function first -- measures its length so as to set a Content-Length header instead of -- using HTTP chunk encoding. mkContentLenResp :: Monad m => HttpStatus -> String -> ByteString -> HttpResp m -- | Make an HttpResp of an arbitrary content-type based on an -- Onum that will dynamically generate the message body. Since the -- message body is generated dynamically, the reply will use an HTTP -- chunk encoding. mkOnumResp :: Monad m => HttpStatus -> String -> Onum ByteString m (IterR ByteString m ()) -> HttpResp m -- | Generate a 301 (redirect) response. resp301 :: Monad m => String -> HttpResp m -- | Generate a 303 (see other) response. resp303 :: Monad m => String -> HttpResp m -- | Generate a 403 (forbidden) response. resp403 :: Monad m => HttpReq -> HttpResp m -- | Generate a 404 (not found) response. resp404 :: Monad m => HttpReq -> HttpResp m -- | Generate a 405 (method not allowed) response. resp405 :: Monad m => HttpReq -> HttpResp m -- | Generate a 500 (internal server error) response. resp500 :: Monad m => String -> HttpResp m -- | Format and enumerate a response header and body. enumHttpResp :: Monad m => HttpResp m -> Maybe UTCTime -> Onum ByteString m () -- | Given the headers of an HTTP request, provides an iteratee that will -- process the request body (if any) and return a response. type HttpRequestHandler m = HttpReq -> Iter ByteString m (HttpResp m) -- | Data structure describing the configuration of an HTTP server for -- inumHttpServer. data HttpServerConf m HttpServerConf :: !String -> Iter ByteString m () -> !Iter ByteString m (Maybe UTCTime) -> !HttpRequestHandler m -> HttpServerConf m srvLogger :: HttpServerConf m -> !String -> Iter ByteString m () srvDate :: HttpServerConf m -> !Iter ByteString m (Maybe UTCTime) srvHandler :: HttpServerConf m -> !HttpRequestHandler m -- | Generate a null HttpServerConf structure with no logging and no -- Date header. nullHttpServer :: Monad m => HttpRequestHandler m -> HttpServerConf m -- | Generate an HttpServerConf structure that uses IO calls to log -- to standard error and get the current time for the Date header. ioHttpServer :: MonadIO m => HttpRequestHandler m -> HttpServerConf m -- | An Inum that behaves like an HTTP server. The file -- Examples/httptest.hs that comes with the iterIO distribution -- gives an example of how to use this function. inumHttpServer :: Monad m => HttpServerConf m -> Inum ByteString ByteString m () instance Typeable HttpReq instance Show HttpReq instance Show FormField instance Show HttpStatus instance Show (HttpResp m) module Data.IterIO.HttpRoute -- | Simple HTTP request routing structure for inumHttpServer. This -- is a wrapper around a function on HttpReq structures. If the -- function accepts the HttpReq, it returns Just a response -- action. Otherwise it returns Nothing. -- -- HttpRoute is a Monoid, and hence can be concatenated -- with mappend or mconcat. For example, you can say -- something like: -- --
-- simpleServer :: Iter L.ByteString IO () -- Output to web browser
-- -> Onum L.ByteString IO () -- Input from web browser
-- -> IO ()
-- simpleServer iter enum = enum |$ inumHttpServer server .| iter
-- where htdocs = "/var/www/htdocs"
-- server = ioHttpServer $ runHttpRoute routing
-- routing = mconcat [ routeTop $ routeConst $ resp301 "/start.html"
-- , routeName "apps" $ routeMap apps
-- , routeFileSys mimeMap "index.html" htdocs
-- ]
-- apps = [ ("app1", routeFn app1)
-- , ("app2", routeFn app2) ]
--
-- app1 :: (Monad m) => HttpReq -> Iter L.ByteString m (HttpResp m)
-- app1 = ...
--
--
-- The above function will redirect requests for / to the URL
-- /start.html using an HTTP 301 (Moved Permanently) response.
-- Any request for a path under apps will be redirected
-- to the functions app1, app2, etc. Finally, any other
-- file name will be served out of the file system under the
-- "/var/www/htdocs" directory. (This example assumes
-- mimeMap has been constructed as discussed for
-- mimeTypesI.)
newtype HttpRoute m
HttpRoute :: (HttpReq -> Maybe (Iter ByteString m (HttpResp m))) -> HttpRoute m
runHttpRoute :: Monad m => HttpRoute m -> HttpReq -> Iter ByteString m (HttpResp m)
-- | Prepend a header field to the response produced by an HttpRoute
-- if that HttpRoute is successful. For example, to let clients
-- cache static data for an hour, you might use:
--
-- -- addHeader (pack "Cache-control: max-age=3600") $ -- routeFileSys mime (dirRedir "index.html") "/var/www/htdocs" --addHeader :: Monad m => ByteString -> HttpRoute m -> HttpRoute m -- | Route all requests to a constant response action that does not depend -- on the request. This route always succeeds, so anything -- mappended will never be used. routeConst :: Monad m => HttpResp m -> HttpRoute m -- | Route all requests to a particular function. This route always -- succeeds, so anything mappended will never be used. routeFn :: (HttpReq -> Iter ByteString m (HttpResp m)) -> HttpRoute m -- | Select a route based on some arbitrary function of the request. For -- most purposes, the existing predicates (routeName, -- routePath, etc.) should be fine, but occationally you might -- want to define a custom predicate. For example, to reject methods -- other then "GET" or "POST" at the top of your route, you could say: -- --
-- myRoute = mconcat [ rejectBadMethod
-- , otherRoute1
-- , ...
-- ]
-- ...
--
-- rejectBadMethod :: HttpRoute m
-- rejectBadMethod =
-- routeReq $ req ->
-- case reqMethod req of
-- s | s == pack "GET" || s == pack "PUT" ->
-- mempty {- reject route, falling through
-- to rest of myRoute -}
-- _ -> routeConst $ resp405 req {- reject request -}
--
routeReq :: (HttpReq -> HttpRoute m) -> HttpRoute m
-- | Route based on the method (GET, POST, HEAD, etc.) in a request.
routeMethod :: String -> HttpRoute m -> HttpRoute m
-- | Route requests whose "Host:" header matches a particular string.
routeHost :: String -> HttpRoute m -> HttpRoute m
-- | Route the root directory (/).
routeTop :: HttpRoute m -> HttpRoute m
-- | Type alias for the argument of routeMap.
type HttpMap m = [(String, HttpRoute m)]
-- | routeMap builds an efficient map out of a list of
-- (directory_name, HttpRoute) pairs. It matches all
-- requests and returns a 404 error if there is a request for a name not
-- present in the map.
routeMap :: Monad m => HttpMap m -> HttpRoute m
-- | routeMap' is like routeMap, but only matches names
-- that exist in the map. Thus, multiple routeMap' results can
-- be combined with mappend. By contrast, combining
-- routeMap results with mappend is useless--the first
-- one will match all requests (and return a 404 error for names that do
-- not appear in the map).
routeMap' :: HttpMap m -> HttpRoute m
-- | Routes a specific directory name, like routeMap for a singleton
-- map.
routeName :: String -> HttpRoute m -> HttpRoute m
-- | Routes a specific path, like routeName, except that the path
-- can include several directories.
routePath :: String -> HttpRoute m -> HttpRoute m
-- | Matches any directory name, but additionally pushes it onto the front
-- of the reqPathParams list in the HttpReq structure. This
-- allows the name to serve as a variable argument to the eventual
-- handling function.
routeVar :: HttpRoute m -> HttpRoute m
-- | Parses mime.types file data. Returns a function mapping file
-- suffixes to mime types. The argument is a default mime type for
-- suffixes to do not match any in the mime.types data. (Reasonable
-- defaults might be "text/html", "text/plain", or,
-- more pedantically but less usefully,
-- "application/octet-stream".)
--
-- Since this likely doesn't change, it is convenient just to define it
-- once in your program, for instance with something like:
--
-- -- mimeMap :: String -> S8.ByteString -- mimeMap = unsafePerformIO $ do -- path <- findMimeTypes ["mime.types" -- , "/etc/mime.types" -- , "/var/www/conf/mime.types"] -- enumFile path |$ mimeTypesI "application/octet-stream" -- where -- findMimeTypes (h:t) = do exist <- fileExist h -- if exist then return h else findMimeTypes t -- findMimeTypes [] = return "mime.types" -- cause error --mimeTypesI :: Monad m => String -> Iter ByteString m (String -> ByteString) -- | dirRedir indexFileName redirects requests to the URL formed -- by appending "/" ++ indexFileName to the requested URL. dirRedir :: Monad m => FilePath -> FilePath -> HttpRoute m -- | Route a request to a directory tree in the file system. It gets the -- Content-Length from the target file's attributes (after opening the -- file). Thus, overwriting files on an active server could cause -- problems, while renaming new files into place should be safe. routeFileSys :: MonadIO m => (String -> ByteString) -> (FilePath -> HttpRoute m) -> FilePath -> HttpRoute m -- | An abstract representation of file system calls returning an opaque -- handle type h in an Iter parameterized by an arbitrary -- Monad m. This representation allows one to use -- routeGenFileSys in a monad that is not an instance of -- MonadIO. data FileSystemCalls h m FileSystemCalls :: !FilePath -> Iter ByteString m FileStatus -> !FilePath -> Iter ByteString m h -> !h -> Iter ByteString m () -> !h -> Iter ByteString m FileStatus -> !h -> Iter ByteString m (Onum ByteString m (IterR ByteString m ())) -> FileSystemCalls h m -- | Return file attributes. fs_stat :: FileSystemCalls h m -> !FilePath -> Iter ByteString m FileStatus -- | Open file and return an opaque handle of type h. fs_open :: FileSystemCalls h m -> !FilePath -> Iter ByteString m h -- | Close an open file. You must call this unless you apply the enumerator -- returned by fs_enum. fs_close :: FileSystemCalls h m -> !h -> Iter ByteString m () -- | Return the attributes of an open file. fs_fstat :: FileSystemCalls h m -> !h -> Iter ByteString m FileStatus -- | Enumerate the contents of an open file, then close the file. If you -- apply the Onum returned by fs_enum, you do not need to -- call fs_close. fs_enum :: FileSystemCalls h m -> !h -> Iter ByteString m (Onum ByteString m (IterR ByteString m ())) -- | A generalized version of routeFileSys that takes a -- FileSystemCalls object and can therefore work outside of the -- MonadIO monad. Other than the FileSystemCalls object, -- the arguments and their meaning are identical to routeFileSys. routeGenFileSys :: Monad m => FileSystemCalls h m -> (String -> ByteString) -> (FilePath -> HttpRoute m) -> FilePath -> HttpRoute m instance Monoid (HttpRoute m)