HaTeX-3.17.2.0: Text/LaTeX/Base/Writer.hs
{-# LANGUAGE TypeFamilies, CPP #-}
-- | The writer monad applied to 'LaTeX' values. Useful to compose 'LaTeX' values
-- using the @do@ notation:
--
-- > anExample :: Monad m => LaTeXT m ()
-- > anExample = do
-- > documentclass [] article
-- > author "Daniel Monad"
-- > title "LaTeX and do notation"
-- > document $ do
-- > maketitle
-- > section "Some words"
-- > "Using " ; texttt "do" ; " notation "
-- > "you avoid many ocurrences of the "
-- > texttt "(<>)" ; " operator and a lot of "
-- > "parentheses. With the cost of a monad."
--
-- Since 'LaTeXT' is a monad transformer, you can do also:
--
-- > anotherExample :: LaTeXT IO ()
-- > anotherExample = lift (readFileTex "foo") >>= verbatim
--
-- This way, it is easy (without carrying arguments) to include IO outputs
-- in the LaTeX document, like files, times or random objects.
--
-- Another approach could be to have custom counters, label management
-- or any other user-defined feature.
--
-- Of course, you can always use the simpler interface provided by the plain 'LaTeX' type.
--
module Text.LaTeX.Base.Writer
( -- * @LaTeXT@ writer
LaTeXT
, runLaTeXT
, execLaTeXT
-- ** Synonyms
, LaTeXT_
, LaTeXM
, runLaTeXM
, execLaTeXM
-- * Utils
, execLaTeXTWarn
, extractLaTeX
, extractLaTeX_
, textell
, rendertexM
, liftFun
, liftOp
, mapLaTeXT
-- * Re-exports
, lift
, liftIO
) where
-- base
import Control.Monad (liftM, liftM2)
import Control.Arrow
import Data.String
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid
import Control.Applicative
#endif
-- transformers
import Control.Monad.Trans.Writer
import Control.Monad.IO.Class
import Control.Monad.Trans.Class
import Data.Functor.Identity
-- HaTeX
import Text.LaTeX.Base.Syntax
import Text.LaTeX.Base.Class
import Text.LaTeX.Base.Render
import Text.LaTeX.Base.Warnings (Warning,checkAll,check)
-- | 'WriterT' monad transformer applied to 'LaTeX' values.
newtype LaTeXT m a =
LaTeXT { unwrapLaTeXT :: WriterT LaTeX m a }
instance Functor f => Functor (LaTeXT f) where
fmap f = LaTeXT . fmap f . unwrapLaTeXT
instance Applicative f => Applicative (LaTeXT f) where
pure = LaTeXT . pure
(LaTeXT f) <*> (LaTeXT x) = LaTeXT $ f <*> x
-- | Type synonym for empty 'LaTeXT' computations.
type LaTeXT_ m = LaTeXT m ()
-- | The 'LaTeXT' monad transformed applied to 'Identity'.
type LaTeXM = LaTeXT Identity
-- | A particular case of 'runLaTeXT'.
--
-- > runLaTeXM = runIdentity . runLaTeXT
--
runLaTeXM :: LaTeXM a -> (a, LaTeX)
runLaTeXM = runIdentity . runLaTeXT
-- | A particular case of 'execLaTeXT'.
--
-- > execLaTeXM = runIdentity . execLaTeXT
--
execLaTeXM :: LaTeXM a -> LaTeX
execLaTeXM = runIdentity . execLaTeXT
instance MonadTrans LaTeXT where
lift = LaTeXT . lift
instance Monad m => Monad (LaTeXT m) where
return = LaTeXT . return
(LaTeXT c) >>= f = LaTeXT $ do
a <- c
let LaTeXT c' = f a
c'
fail = return . error
instance MonadIO m => MonadIO (LaTeXT m) where
liftIO = lift . liftIO
instance (Monad m, a ~ ()) => LaTeXC (LaTeXT m a) where
liftListL f xs = mapM extractLaTeX_ xs >>= textell . f
-- | Running a 'LaTeXT' computation returns the final 'LaTeX' value.
runLaTeXT :: LaTeXT m a -> m (a,LaTeX)
runLaTeXT = runWriterT . unwrapLaTeXT
-- | This is the usual way to run the 'LaTeXT' monad
-- and obtain a 'LaTeX' value.
--
-- > execLaTeXT = liftM snd . runLaTeXT
--
-- If @anExample@ is defined as above (at the top of this module
-- documentation), use the following to get the LaTeX value
-- generated out.
--
-- > myLaTeX :: Monad m => m LaTeX
-- > myLaTeX = execLaTeXT anExample
--
execLaTeXT :: Monad m => LaTeXT m a -> m LaTeX
execLaTeXT = liftM snd . runLaTeXT
-- | Version of 'execLaTeXT' with possible warning messages.
-- This function applies 'checkAll' to the 'LaTeX' output.
execLaTeXTWarn :: Monad m => LaTeXT m a -> m (LaTeX,[Warning])
execLaTeXTWarn = liftM (id &&& check checkAll) . execLaTeXT
-- | This function run a 'LaTeXT' computation,
-- lifting the result again in the monad.
extractLaTeX :: Monad m => LaTeXT m a -> LaTeXT m (a,LaTeX)
extractLaTeX = LaTeXT . lift . runWriterT . unwrapLaTeXT
-- | Executes a 'LaTeXT' computation, embedding it again in
-- the 'LaTeXT' monad.
--
-- > extractLaTeX_ = liftM snd . extractLaTeX
--
-- This function was heavily used in the past by HaTeX-meta
-- to generate those @.Monad@ modules. The current purpose
-- is to implement the 'LaTeXC' instance of 'LaTeXT', which
-- is closely related.
extractLaTeX_ :: Monad m => LaTeXT m a -> LaTeXT m LaTeX
extractLaTeX_ = liftM snd . extractLaTeX
-- | With 'textell' you can append 'LaTeX' values to the
-- state of the 'LaTeXT' monad.
textell :: Monad m => LaTeX -> LaTeXT m ()
textell = LaTeXT . tell
-- | Lift a function over 'LaTeX' values to a function
-- acting over the state of a 'LaTeXT' computation.
liftFun :: Monad m
=> (LaTeX -> LaTeX)
-> (LaTeXT m a -> LaTeXT m a)
liftFun f (LaTeXT c) = LaTeXT $ do
(p,l) <- lift $ runWriterT c
tell $ f l
return p
-- | Lift an operator over 'LaTeX' values to an operator
-- acting over the state of two 'LaTeXT' computations.
--
-- /Note: The returned value is the one returned by the/
-- /second argument of the lifted operator./
liftOp :: Monad m
=> (LaTeX -> LaTeX -> LaTeX)
-> (LaTeXT m a -> LaTeXT m b -> LaTeXT m b)
liftOp op (LaTeXT c) (LaTeXT c') = LaTeXT $ do
(_,l) <- lift $ runWriterT c
(p,l') <- lift $ runWriterT c'
tell $ l `op` l'
return p
-- | A helper function for building monad transformers, e.g.
--
-- > instance MonadReader r m => MonadReader r (LaTeXT m) where
-- > ask = lift ask
-- > local = mapLaTeXT . local
--
-- This declaration could be included here, but it would add a
-- dependency on mtl.
mapLaTeXT :: (m (a, LaTeX) -> m (a, LaTeX)) -> LaTeXT m a -> LaTeXT m a
mapLaTeXT f = LaTeXT . mapWriterT f . unwrapLaTeXT
-- | Just like 'rendertex', but with 'LaTeXT' output.
--
-- > rendertexM = textell . rendertex
--
rendertexM :: (Render a, Monad m) => a -> LaTeXT m ()
rendertexM = textell . rendertex
-- Overloaded Strings
instance (Monad m, a ~ ()) => IsString (LaTeXT m a) where
fromString = textell . fromString
-- Monoids
instance (Monad m, Monoid a) => Monoid (LaTeXT m a) where
mempty = return mempty
mappend = liftM2 mappend