I write docs for my iteratee-like library and I have problem with
lhs2tex. When I execute lhs2tex and then latex, I get a document with
long typesignatures and expressions expanded beyound page margins. I
attached a sourcefile I have problem with. Can someone give me an advise
how to force long signatures and expressions to be broken into multible
lines ?
This module contains basic types and combinators for building and processing streaming data
Note: any data constructor and field names may change. So, do NOT use them in your code, use only
defined functions and instances.
\begin{code}
module Data.Iteration.Types where
\end{code}
We wish full-fledged monad transformers. This imports classes from transformers package.
\begin{code}
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
\end{code}
Enumerator is something that may provide input element or input tail. Enumerator is
newtype for monadic action, that can result in
Nothing - no more input
Just (Left someError) - error occured
Just (Right s, t) - some output provided and computation for source tail provided
\begin{code}
newtype Enumerator e s m
= Enumerator
{ runEnumerator ::
m (Maybe (Either e (s, Enumerator e s m) ) )
}
\end{code}
the following function is a wrapper so resulting interface for different modules be compatible.
It gets three computations: what to do if no input, what to do on error and
how to produce one step of input. Tail of input is reduced in the same computation
\begin{code}
runE :: Monad m => Enumerator e s m -> m r -> (e -> m r) -> (s -> Enumerator e s m -> m r) -> m r
runE en pr eh sh = runEnumerator en >>= \v -> case v of
Nothing -> pr
Just (Left e) -> eh e
Just (Right (s,e) ) -> sh s e
\end{code}
Enumeratee is a tool for building enumerator. It is separate from enumerators to not mess the logic.
A way to embed computations in nested monad is needed. A nested monad appears in enumerator only, so
to lift from inner monad we have to enclose computation into resulting enumerator.
\begin{code}
newtype Enumeratee e s m a
= Enumeratee
{ runEnumeratee
:: (a -> Enumerator e s m)
-> Enumerator e s m
}
\end{code}
Of course, a way to build Enumerator from Enumeratee is needed.
\begin{code}
mkE :: Enumeratee e s m (Enumerator e s m) -> Enumerator e s m
mkE e = runEnumeratee e id
\end{code}
A Monad and Functor instances for Continuation monad are adopted brain-free
\begin{code}
instance Monad (Enumeratee e s m) where
return a = Enumeratee ($ a)
m >>= k = Enumeratee $ \c -> runEnumeratee m $ \v -> runEnumeratee (k v) c
instance Functor (Enumeratee e s m) where
fmap f m = m >>= return . f
\end{code}
To embed computation in inner monad, we have to dig into Enumerator as Enumeratee's newtype does not
contain explicit monadic computations. This implementation runs nested computation and then passes
result to continuation and opens newtype.
\begin{code}
instance MonadTrans (Enumeratee e s) where
lift m = Enumeratee $ \c -> Enumerator $ m >>= \v -> runEnumerator (c v)
\end{code}
A very traditional MonadIO instance, found as is in most monad transformers libraries.
\begin{code}
instance MonadIO m => MonadIO (Enumeratee e s m) where
liftIO = lift. liftIO
\end{code}
The main part: yield a value. We postfix function with E (Enumeratee) to distinct from
similiar functions found later.
\begin{code}
yieldE :: Monad m => s -> Enumeratee e s m ()
yieldE v = Enumeratee $ \c -> Enumerator $ return $ Just $ Right (v, c () )
\end{code}
finalE is final computations returns 'dummy' enumerator. It is designed to be
last computation in do-block in Enumerator specification. An alternative exists:
failE, that indicates an error in stream.
\begin{code}
finalE :: Monad m => Enumeratee e s m (Enumerator e s m)
finalE = return $ Enumerator $ return Nothing
failE :: Monad m => e -> Enumeratee e s m (Enumerator e s m)
failE e = return $ Enumerator $ return $ Just $ Left e
\end{code}
Iterator is something, that takes enumerator and produces value. Unlike Enumerator, Iterator
is in control: it reduces enumerator whenever needed. However, basic definitions follows the
same pattern.
\begin{code}
newtype Iterator r e s m
= Iterator
{ runIterator
:: Enumerator e s m
-> m r
}
newtype Iteratee r e s m a
= Iteratee
{ runIteratee
:: (a -> Iterator r e s m)
-> Iterator r e s m
}
mkI :: Iteratee r e s m (Iterator r e s m) -> Iterator r e s m
mkI i = runIteratee i id
instance Monad (Iteratee r e s m) where
return a = Iteratee ($ a)
m >>= k = Iteratee $ \c -> runIteratee m $ \v -> runIteratee (k v) c
instance Functor (Iteratee r e s m) where
fmap f m = m >>= return . f
instance MonadTrans (Iteratee r e s) where
lift m = Iteratee $ \c -> Iterator $ \e -> m >>= \v -> runIterator (c v) e
instance MonadIO m => MonadIO (Iteratee r e s m) where
liftIO = lift . liftIO
\end{code}
This function produces final iterator that does nothing and simply returns value provided
\begin{code}
finalI :: Monad m => r -> Iteratee r e s m (Iterator r e s m)
finalI r = return $ Iterator $ \_ -> return r
\end{code}
This is the main operation. As with yieldE, we have to dig into iterator. Sematic of operation is
clear: give action in underiling monad to execute if no input, give action to execute if error
occurs and you are free to describe tail of computation.
\begin{code}
nextI :: Monad m => m r -> (e -> m r) -> Iteratee r e s m s
nextI pr eh = Iteratee $ \c -> Iterator $ \e -> runE e pr eh $ \s e' -> runIterator (c s) e'
\end{code}
----------------------------------------------------------------------------------------
One sometimes needs to transcode streams. This is not simple fmap operation for functors, but
a full fledged transformation, where one free to take any amount of input and produce any amount
of output. The simplest form of such transformation is lexing a source file.
\begin{code}
newtype Adapter eo ei so si m
= Adapter
{ runAdapter
:: Enumerator ei si m
-> Enumerator eo so m
}
\end{code}
yet again boring part.
\begin{code}
newtype Adaptee eo ei so si m a
= Adaptee
{ runAdaptee
:: ( a -> Adapter eo ei so si m)
-> Adapter eo ei so si m
}
mkA :: Adaptee eo ei so si m (Adapter eo ei so si m)
-> Adapter eo ei so si m
mkA a = runAdaptee a id
instance Monad (Adaptee eo ei so si m) where
return a = Adaptee ($ a)
m >>= k = Adaptee $ \c -> runAdaptee m $ \v -> runAdaptee (k v) c
instance Functor (Adaptee eo ei so si m) where
fmap f m = m >>= return. f
instance MonadIO m => MonadIO (Adaptee eo ei so si m) where
liftIO = lift . liftIO
\end{code}
Lifting of computation in underling monad for Adaptee is slightly tricker. As both Adaptee and Adapter
does not contain monadic computations directly, we have to dig into Enumerator.
\begin{code}
instance MonadTrans (Adaptee eo ei so si) where
lift m = Adaptee $ \c -> Adapter $ \e -> Enumerator $ m >>= \v -> runEnumerator $ runAdapter (c v) e
\end{code}
taking input from enumerator follows the same pattern as for iteratee: give enumerator if no input,
give enumerator for error in input. And of cource, again digging into enumerator
\begin{code}
nextA :: Monad m
=> Enumerator eo so m
-> (ei -> Enumerator eo so m)
-> Adaptee eo ei so si m si
nextA ote oeh = Adaptee $ \c -> Adapter $ \en -> Enumerator $ runEnumerator en >>= \cev -> case cev of
Nothing -> runEnumerator ote
Just (Left er) -> runEnumerator $ oeh er
Just (Right (v,st) ) -> runEnumerator $ runAdapter (c v) st
\end{code}
yielding a value is straight.
\begin{code}
yieldA :: Monad m
=> so
-> Adaptee eo ei so si m ()
yieldA v = Adaptee $ \c -> Adapter $ \e -> Enumerator $ return $ Just $ Right $ (v , runAdapter (c () ) e )
\end{code}
buiding final computation is simple: provide 'tail' and get final adapter, that drops any remaining input and
simply runs given enumerator
\begin{code}
finalA :: Monad m
=> Enumerator eo so m
-> Adaptee eo ei so si m (Adapter eo ei so si m)
finalA e = return $ Adapter $ \_ -> e
\end{code}
Zipper is very similiar to Adapter: it takes two Enumerators and returns one Enumerator.
\begin{code}
data Zipper eo eli eri so sli sri m
= Zipper
{ runZipper
:: Enumerator eli sli m
-> Enumerator eri sri m
-> Enumerator eo so m
}
\end{code}
boring, boring, boring
\begin{code}
data Zippee eo eli eri so sli sri m a
= Zippee
{ runZippee
:: (a -> Zipper eo eli eri so sli sri m)
-> Zipper eo eli eri so sli sri m
}
mkZ :: Zippee eo eli eri so sli sri m (Zipper eo eli eri so sli sri m)
-> Zipper eo eli eri so sli sri m
mkZ z = runZippee z id
instance Monad (Zippee eo eli eri so sli sri m) where
return a = Zippee ($ a)
m >>= k = Zippee $ \c -> runZippee m $ \v -> runZippee (k v) c
instance Functor (Zippee eo eli eri so sli sri m) where
fmap f m = m >>= return . f
instance MonadIO m => MonadIO (Zippee eo eli eri so sli sri m) where
liftIO = lift . liftIO
\end{code}
to implement lifting from inner monad we again have to dig into Enumerator
\begin{code}
instance MonadTrans (Zippee eo eli eri so sli sri) where
lift m = Zippee $ \c -> Zipper $ \len ren -> Enumerator $ m >>= \v -> runEnumerator $ runZipper (c v) len ren
\end{code}
Zipper has two operations for taking next elemen: take element from left stream (nextZl) and take
element from right stream (nextZr). nextZl takes adapter for transcoding rest of the right stream if
no input and if error in left stream and reverse for nextZr.
\begin{code}
nextZl :: Monad m
=> Adapter eo eri so sri m
-> (eli -> Adapter eo eri so sri m)
-> Zippee eo eli eri so sli sri m sli
nextZl dra era = Zippee $ \c -> Zipper $ \len ren -> Enumerator $ runEnumerator len >>= \cev -> case cev of
Nothing -> runEnumerator $ runAdapter dra ren
Just (Left e) -> runEnumerator $ runAdapter (era e) ren
Just (Right (v,lre) ) -> runEnumerator $ runZipper (c v) lre ren
nextZr :: Monad m
=> Adapter eo eli so sli m
-> ( eri -> Adapter eo eli so sli m)
-> Zippee eo eli eri so sli sri m sri
nextZr dla ela = Zippee $ \c -> Zipper $ \len ren -> Enumerator $ runEnumerator ren >>= \cev -> case cev of
Nothing -> runEnumerator $ runAdapter dla len
Just (Left e) -> runEnumerator $ runAdapter (ela e) len
Just (Right (v,rre)) -> runEnumerator $ runZipper (c v) len rre
\end{code}
yieldZ is very similiar to yieldA
\begin{code}
yieldZ :: Monad m
=> so
-> Zippee eo eli eri so sli sri m ()
yieldZ so = Zippee $ \c -> Zipper $ \len ren -> Enumerator $ return $ Just $ Right $ (so, runZipper (c () ) len ren )
\end{code}
Zipper provides three finals:: one to drop both input streams, one to drop right and transcode only left
(finalZl) and one to drop left and transcode only right (finalZr)
\begin{code}
finalZ :: Monad m
=> Enumerator eo so m
-> Zippee eo eli eri so sli sri m (Zipper eo eli eri so sli sri m )
finalZ e = Zippee $ \c -> c $ Zipper $ \_ _ -> e
finalZl :: Monad m
=> Adapter eo eli so sli m
-> Zippee eo eli eri so sli sri m (Zipper eo eli eri so sli sri m)
finalZl a = Zippee $ \c -> c $ Zipper $ \len _ -> runAdapter a len
finalZr :: Monad m
=> Adapter eo eri so sri m
-> Zippee eo eli eri so sli sri m (Zipper eo eli eri so sli sri m)
finalZr a = Zippee $ \c -> c $ Zipper $ \_ ren -> runAdapter a ren
\end{code}
Passive iterator is somthing, that encodes iteration, but has to be driven by
external function. It represents one step of folding, where next steps are encoded
as suspended monadic actions. This one has straighword design to imitate interface
of active iteratees.
\begin{code}
newtype IteratorP r e s m
= IteratorP
{ runIteratorP :: m ( Either (m r) (m r, e -> m r, s -> IteratorP r e s m) )
}
\end{code}
boring, boring, boring
\begin{code}
newtype IterateeP r e s m a
= IterateeP
{ runIterateeP :: (a -> IteratorP r e s m)
-> IteratorP r e s m
}
instance Monad (IterateeP r e s m) where
return a = IterateeP ($ a)
m >>= k = IterateeP $ \c -> runIterateeP m $ \v -> runIterateeP (k v) c
instance Functor (IterateeP r e s m) where
fmap f m = m >>= return . f
instance MonadTrans (IterateeP r e s) where
lift m = IterateeP $ \c -> IteratorP $ m >>= \v -> runIteratorP $ c v
instance MonadIO m => MonadIO (IterateeP r e s m) where
liftIO = lift . liftIO
\end{code}
imitate interface for active iterators
\begin{code}
nextIP :: Monad m
=> m r
-> (e -> m r)
-> IterateeP r e s m s
nextIP pr eh = IterateeP $ \c -> IteratorP $ return $ Right $ (pr, eh , c)
finalIP :: Monad m
=> r
-> IterateeP r e s m (IteratorP r e s m)
finalIP r = IterateeP $ \c -> c $ IteratorP $ return $ Left $ return r
\end{code}
promote passive iterator to active one.
\begin{code}
ip2a :: Monad m
=> IteratorP r e s m
-> Iterator r e s m
ip2a i = Iterator $ \e -> do
is <- runIteratorP i
case is of
Left sa -> sa
Right (fa,eh,sh) -> runE e fa eh $ \s en -> runIterator (ip2a (sh s)) en
\end{code}
combine two passive iterators into one.
\begin{code}
parIP :: Monad m
=> IteratorP r1 e s m
-> IteratorP r2 e s m
-> IteratorP (r1,r2) e s m
parIP i1 i2 = IteratorP $ do
i1s <- runIteratorP i1
i2s <- runIteratorP i2
case (i1s,i2s) of
(Left m1s, Left m2s) -> do
m1pr <- m1s
m2pr <- m2s
return $ Left $ return (m1pr,m2pr)
(Right (m1r,m1e,m1s), Left m2r) -> return $ Right
( m1r >>= \r1 -> m2r >>= \r2 -> return (r1,r2)
, \e -> m1e e >>= \r1 -> m2r >>= \r2 -> return (r1,r2)
, \s -> parIP (m1s s) (IteratorP $ return $ Left $ m2r)
)
(Left m1r, Right (m2r,m2e,m2s) ) -> return $ Right
( m1r >>= \r1 -> m2r >>= \r2 -> return (r1,r2)
, \e -> m1r >>= \r1 -> m2e e >>= \r2 -> return (r1,r2)
, \s -> parIP (IteratorP $ return $ Left $ m1r) (m2s s)
)
(Right (m1r,m1e,m1s), Right (m2r,m2e,m2s) ) -> return $ Right
( m1r >>= \r1 -> m2r >>= \r2 -> return (r1,r2)
, \e -> m1e e >>= \r1 -> m2e e >>= \r2 -> return (r1,r2)
, \s -> parIP (m1s s) (m2s s)
)
\end{code}
Future work:: addition of resource management helper support.
_______________________________________________
Haskell-Cafe mailing list
[email protected]
http://www.haskell.org/mailman/listinfo/haskell-cafe