HaTeX User's Guide
From HaskellWiki
(First (and incomplete) edition. Testing the wiki backend of the guide.) |
(Updated.) |
||
| Line 57: | Line 57: | ||
The function that avoids reserved characteres is exported with the name <hask>protectString</hask>. Also, there is a variant for <hask>Text</hask> values called <hask>protectText</hask>. | The function that avoids reserved characteres is exported with the name <hask>protectString</hask>. Also, there is a variant for <hask>Text</hask> values called <hask>protectText</hask>. | ||
| - | The use of the <hask>IsString</hask> class is because the ''Overloaded Strings'' extension. This one is similar to the ''Overloaded Numbers'' Haskell feature, which translates the number<hask>4</hask> to <hask>fromInteger 4</hask>. In a similar way, with <hask>OverloadedStrings</hask> enabled, the string<hask>"foo"</hask> is translated to <hask>fromString "foo"</hask>. If we now apply this to our blocks, the string <hask>"foo"</hask> will be automatically translated to a | + | The use of the <hask>IsString</hask> class is because the ''Overloaded Strings'' extension. This one is similar to the ''Overloaded Numbers'' Haskell feature, which translates the number<hask>4</hask> to <hask>fromInteger 4</hask>. In a similar way, with <hask>OverloadedStrings</hask> enabled, the string<hask>"foo"</hask> is translated to <hask>fromString "foo"</hask>. If we now apply this to our blocks, the string <hask>"foo"</hask> will be automatically translated to a <hask>latex</hask> block with ''foo'' as content. Quite handy! We will assume the <hask>OverloadedStrings</hask> extension enabled from now. |
====More blocks==== | ====More blocks==== | ||
| - | There is a lot of functions for create blocks. In fact, we can say that this is the main purpose of the library. LaTeX has a lot of commands, in order to set font attributes, create tables, insert graphics, include mathematical symbols, | + | There is a lot of functions for create blocks. In fact, we can say that this is the main purpose of the library. LaTeX has a lot of commands, in order to set font attributes, create tables, insert graphics, include mathematical symbols, etc. So HaTeX have a function for each command defined in LaTeX (to tell the truth, only for a small subset). Please, go to the API documentation to read about particular functions. Build it locally or find it in Hackage: http://hackage.haskell.org/package/HaTeX. You will find the class constraint <hask>LaTeXC l</hask> in every entity. <hask>LaTeX</hask> is an instance of this class, so you can assume that <hask>l</hask> is the <hask>LaTeX</hask> datatype without any problem. More about this in section about the <hask>LaTeXC</hask> class. |
===Putting blocks together=== | ===Putting blocks together=== | ||
| Line 99: | Line 99: | ||
As always, the best way to learn something well is to try it by yourself. Since to see code examples can give you a great help, HaTeX comes with several examples where you can see by yourself how to get the work done. | As always, the best way to learn something well is to try it by yourself. Since to see code examples can give you a great help, HaTeX comes with several examples where you can see by yourself how to get the work done. | ||
| - | The API reference is also a good point to keep in mind. Descriptions of functions make you know how exactly they works. And, when they are not present, function names with type signatures may be | + | The API reference is also a good point to keep in mind. Descriptions of functions make you know how exactly they works. And, when they are not present, function names with type signatures may be very helpful and descriptive. |
| + | |||
| + | ==LaTeX blocks and the Writer monad== | ||
| + | |||
| + | ===The Writer Monad=== Fixed a monoid <hask>M</hask>, the <hask>M</hask>-writer monad is just all possible pairs of elements from <hask>M</hask> and elements from other types. Thus, the Haskell declaration is as follows[[#Footnotes|<sup>4</sup>]]:<haskell> | ||
| + | data W m a = W m a | ||
| + | </haskell> Note that to get the monad we need to fix the type <hask>m</hask> (kind of monads is <hask>* -> *</hask>). To inject an arbitrary value into the monad (the Haskell <hask>return</hask> function) we use the neutral element (<hask>mempty</hask>) of the monoid.<haskell> | ||
| + | inject :: Monoid m => a -> W m a | ||
| + | inject a = W mempty a | ||
| + | </haskell> Think that no other element of <hask>m</hask> is possible to think: it is the only element we know of it! Like any other monad, <hask>W m</hask> is also a <hask>Functor</hask>. We just apply the function to the value.<haskell> | ||
| + | instance Functor (W m) where | ||
| + | fmap f (W m a) = W m (f a) | ||
| + | </haskell> Every <hask>Monad</hask> instance can be given by the two monad operations <hask>inject</hask> and <hask>join</hask>. We already defined the <hask>inject</hask> function. The other one deletes one monad type constructor.<haskell> | ||
| + | join :: Monoid m => W m (W m a) -> W m a | ||
| + | join (W m (W m' a)) = W (mappend m m') a | ||
| + | </haskell> In this function we use the other <hask>Monoid</hask> method to combine both values. It is important to note that in both monad operations <hask>inject</hask> and <hask>join</hask> we used <hask>mempty</hask> and <hask>mappend</hask> respectively. In practice, this is because they act equal. Indeed, they are equal if we forget the<hask>a</hask> value. Now, we are ready to define the <hask>Monad</hask> instance:<haskell> | ||
| + | instance Monoid m => Monad (W m) where | ||
| + | return = inject | ||
| + | w >>= f = join (fmap f w) | ||
| + | </haskell> There is nothing to say about this instance. It is and standard definition valid to any monad. | ||
| + | |||
| + | What we have done here is to hide in a monad a monoid with all its operations. We have created a machine that operates monoid values. To insert a value into the machine we need the <hask>tell</hask> function:<haskell> | ||
| + | tell :: m -> W m () | ||
| + | tell m = W m () | ||
| + | </haskell> When we execute the machine, it returns to us the result of operate all the values we have put on it.<haskell> | ||
| + | execute :: W m a -> m | ||
| + | execute (W m a) = m | ||
| + | </haskell> Let's see the machine working. For example, the <hask>Int</hask> type with addition forms a <hask>Monoid</hask>.<haskell> | ||
| + | instance Monoid Int where | ||
| + | mempty = 0 | ||
| + | mappend = (+) | ||
| + | |||
| + | example :: Int | ||
| + | example = execute $ do | ||
| + | tell 1 | ||
| + | tell 2 | ||
| + | tell 3 | ||
| + | tell 4 | ||
| + | </haskell> When we evaluate <hask>example</hask> we get <hask>10</hask>, as expected. Using <hask>mapM_</hask> we can rewrite <hask>example</hask>.<haskell> | ||
| + | example :: Int | ||
| + | example = execute $ mapM_ tell [ 1 .. 4 ] | ||
| + | </haskell> | ||
| + | |||
| + | ===The LaTeX Monad=== | ||
| + | |||
| + | Let's go back to the <hask>LaTeX</hask> type. Since <hask>LaTeX</hask> is an instance of <hask>Monoid</hask> we can construct its correspondent <hask>Writer</hask> monad.<haskell> | ||
| + | type LaTeXW = W LaTeX | ||
| + | </haskell> The <hask>W</hask> machine is waiting now for <hask>LaTeX</hask> values.<haskell> | ||
| + | example :: LaTeX | ||
| + | example = execute $ do | ||
| + | tell $ documentclass [] article | ||
| + | tell $ author "Monads lover" | ||
| + | tell $ title "LaTeX and the Writer Monad" | ||
| + | </haskell> We put all that blocks in the machine, and it returns the concatenated block. We saved a lot of<hask>mappend</hask>'s, but we now have a lot of <hask>tell</hask>'s. No problem. Just redefine each function of blocks with <hask>tell</hask> and <hask>execute</hask>.<haskell> | ||
| + | author' :: LaTeXW a -> LaTeXW () | ||
| + | author' = tell . author . execute | ||
| + | </haskell> If it is done in a similar way with <hask>documentclass</hask> and <hask>title</hask>, every <hask>tell</hask> in <hask>example</hask> disappears.<haskell> | ||
| + | example :: LaTeX | ||
| + | example = execute $ do | ||
| + | documentclass' [] article | ||
| + | author' "Monads lover" | ||
| + | title' "LaTeX and the Writer Monad" | ||
| + | </haskell> And we can now use the <hask>LaTeX</hask> machine more comfortably. However, we have all functions duplicated. This is why the <hask>LaTeXC</hask> class exists. We are going to talk about it later. | ||
| + | |||
| + | ===Composing monads=== | ||
| + | |||
| + | To add flexibility to HaTeX, the writer monad explained above is defined as a monad transformer, named <hask>LaTeXT</hask>. The way to use it is the same, there are just a few changes. | ||
| + | |||
| + | The first change is in type signatures. We need to carry an inner monad in every type.<haskell> | ||
| + | foo :: Monad m => LaTeXT m a | ||
| + | </haskell> However, in practice, we can avoid it. Say we going to use an specific monad <hask>M</hask>.<haskell> | ||
| + | type LaTeXW = LaTeXT M | ||
| + | |||
| + | foo :: LaTeXW a | ||
| + | </haskell> Now, type signatures remain unchanged. | ||
| + | |||
| + | The other change is a new feature: the <hask>lift</hask> function. With it we can do any computation of our inner monad at any time. For example, suppose we want to output some code we have in the file ''foo.hs''. Instead of copy all its content, or read and carry it as an argument along the code, you can simply read that file using <hask>lift</hask> wherever you want.<haskell> | ||
| + | type LaTeXIO = LaTeXT IO | ||
| + | |||
| + | readCode :: FilePath -> LaTeXIO () | ||
| + | readCode fp = lift (readFileTex fp) >>= verbatim . raw | ||
| + | |||
| + | example :: LaTeXIO () | ||
| + | example = do | ||
| + | "This is the code I wrote this morning:" | ||
| + | readCode "foo.hs" | ||
| + | "It was a funny exercise." | ||
| + | </haskell> Different monads will give different features. In the case we are not interested in any of these features, it is enough to use the Identity monad.<haskell> | ||
| + | type LaTeXW = LaTeXT Identity | ||
| + | </haskell> | ||
| + | |||
| + | ==The LaTeXC class== | ||
| + | |||
| + | HaTeX has two different interfaces. One uses blocks as <hask>Monoid</hask> elements and the other as <hask>Monad</hask> actions. If we want to keep both interfaces we have two choices: to duplicate function definitions[[#Footnotes|<sup>5</sup>]] or to have a typeclass which unifies both interfaces. Since duplicate definitions is a hard work and can arise problems[[#Footnotes|<sup>6</sup>]], we took the second alternative and defined the <hask>LaTeXC</hask> typeclass. Both <hask>LaTeX</hask> and <hask>LaTeXT m a</hask> are instances of <hask>LaTeXC</hask> (the second one is a little tricky), so every function in HaTeX is defined using the typeclass. This way, we have both interfaces with a single import, without being worry about maintaining duplicated code. The cost is to have class constraints in type signatures. But these constraints are only required in the package. At the user level, you choose your interface and write type signatures in consequence. | ||
| + | |||
| + | ==Packages== | ||
| + | |||
| + | LaTeX, in addition to its predefined commands, has a big number of packages that increase its power. HaTeX functions for some of these packages are defined in separated modules, one module per package. This way, you can import only those functions you actually need. Some of these modules are below explained. | ||
| + | |||
| + | ===Inputenc=== | ||
| + | |||
| + | This package is of vital importance if you use non-ASCII characters in your document. For example, if my name is ''Ángela'', the ''Á'' character will not appear correctly in the output. To solve this problem, use the <hask>Inputenc</hask> module.<haskell> | ||
| + | import Text.LaTeX.Base | ||
| + | import Text.LaTeX.Packages.Inputenc | ||
| + | |||
| + | thePreamble :: LaTeX | ||
| + | thePreamble = | ||
| + | documentclass [] article | ||
| + | <> usepackage [utf8] inputenc | ||
| + | <> author "Ángela" | ||
| + | <> title "Issues with non-ASCII characters" | ||
| + | </haskell> Don't forget to set to UTF-8 encoding your Haskell source too. | ||
| + | |||
| + | ===Graphicx=== | ||
| + | |||
| + | With the <hask>Graphicx</hask> package you can insert images in your document and do some other transformations. In order to insert an image use the <hask>includegraphics</hask> function.<haskell> | ||
| + | includegraphics :: LaTeXC l => [IGOption] -> FilePath -> l | ||
| + | </haskell> The list of <hask>IGOption</hask>'s allows you to set some properties of the image, like width, height, scaling or rotation. See the API documentation for details. | ||
| + | |||
| + | ==Epilogue== | ||
| + | |||
| + | ===Notes about this guide=== | ||
| + | |||
| + | '''This guide is not static'''. It will be changed, extended and improved with the time. If you think there is something unclear, something hard to understand, please, report it. | ||
| + | |||
| + | ===Notes from the author=== | ||
| + | |||
| + | It has been a long time since I started with the HaTeX project. But it was not until version 3.3 that I feel like it is taking the right way. This is why I did not write this guide before. | ||
| + | |||
| + | I learned a lot during its creation, beginning with a little project that would help myself to write LaTeX documents, and then becoming in my first contact with the Haskell Community. And all the time it was funny. | ||
| + | |||
| + | I would like to end this guide saying thanks to all people that has been interested in HaTeX some way, specially to those that contributed to it with patches, opinions or bug reports. '''Thanks'''. | ||
==Footnotes== | ==Footnotes== | ||
| Line 110: | Line 241: | ||
<sup>3</sup>: From '''GHC 7.4''', <hask>(<>)</hask> is defined as a synonym for <hask>mappend</hask>. For previous | <sup>3</sup>: From '''GHC 7.4''', <hask>(<>)</hask> is defined as a synonym for <hask>mappend</hask>. For previous | ||
versions of GHC, HaTeX exports the synonym. | versions of GHC, HaTeX exports the synonym. | ||
| + | |||
| + | <sup>4</sup>: Some authors write it using tuples, like this: <hask>data W m a = W (a,m)</hask>. | ||
| + | |||
| + | <sup>5</sup>: This was the approach taken in HaTeX 3 until the version 3.3, where the <hask>LaTeXC</hask> class was included. | ||
| + | |||
| + | <sup>6</sup>: In fact, we had a problem with HaTeX-meta, the program that automatically generated the duplicated functions. | ||
| + | The problem was described in a blog post: http://deltadiaz.blogspot.com.es/2012/04/hatex-trees-and-problems.html. | ||
Current revision
Contents |
1 Preface
1.1 Introduction
If you are here because you want to learn more about HaTeX, or just feel curious, you are in the right place. First of all, note that this guide is addressed to that people that already knows the basics of both Haskell and LaTeX. Otherwise, try to learn first a bit of these languages (both are quite useful learnings). To learn Haskell, though I guess you already learned it since you are reading these lines, go to the Haskell web [1] and search for some tutorials or books. To learn LaTeX, you can start withThe not so short introduction to LaTeX [2].
The HaTeX library aspires to be the tool that Haskellers could want to make theirLaTeX things without exit of their language (we understand that is difficult to leave Haskell after the first date), trying to be the most comprehensive and well done as possible. Do you think, anyway, that something could be done better? Perhaps something is lacked? Go then to the HaTeX mailing list [3] and leave your complain without mercy! Or, in the case you are a GitHub user, say your word in the issue list [4] or, to be awesome, make yourself a patch and send a pull request. This is the great thing about open source projects!
1.2 What is HaTeX?
Before we explain how HaTeX works, it is convenient to say what actually HaTeX is.
HaTeX is a Haskell library that provides functions to create, manipulate and parse LaTeX code.
People often says that HaTeX is a LaTeX DSL. With it you can enjoy all the advantages you already have in Haskell while creating LaTeX documents. A common purpose is to automatize the creation of such documents, perhaps from a source data in Haskell. A more exotic one is to render chess tables. Possibilities are in a wide range. The idea is the following: if you can do it with LaTeX, you can do it with HaTeX, but adding all the Haskell features.
2 Basics
Through this section you will learn the basics of HaTeX. Essentially, how it works.
2.1 The Monoid class
If you are already familiar with theclass Monoid m where mempty :: m mappend :: m -> m -> m mconcat :: [m] -> m
xs ++ [] = [] ++ xs = xs
2.2 LaTeX blocks
Suppose we have a well-formed1 piece of LaTeX code, call it a. Now, let2.3 Creating blocks
We have now an universe of blocks forming a monoid. What we need now is a way to create these blocks. As we said, a block is the representation of a well-formed piece of LaTeX code. Letlinespread :: Float -> LaTeX
title :: LaTeX -> LaTeX
2.3.1 From strings
Inserting text in a LaTeX document is a constant task. You can create a block with text given an arbitraryclass IsString a where fromString :: String -> a
2.3.2 More blocks
There is a lot of functions for create blocks. In fact, we can say that this is the main purpose of the library. LaTeX has a lot of commands, in order to set font attributes, create tables, insert graphics, include mathematical symbols, etc. So HaTeX have a function for each command defined in LaTeX (to tell the truth, only for a small subset). Please, go to the API documentation to read about particular functions. Build it locally or find it in Hackage: http://hackage.haskell.org/package/HaTeX. You will find the class constraint2.4 Putting blocks together
Once you have the blocks, as we said before, you need to append them. Themconcat [ "I can see a " , textbf "rainbow" , " in the blue " , textit "sky" , "." ]
2.5 Rendering
This is the last step in our LaTeX document creation. When we have our finalLaTeX blockshort = documentclass [] article <> title "A short message" <> author "John Short" <> document (maketitle <> "This is all.")
\documentclass{article}\title{A short message} \author{John Short} \begin{document} \maketitle{} This is all
\end{document}
renderFile :: Render a => FilePath -> a -> IO ()
class Render a where render :: a -> Text
2.6 Try yourself
As always, the best way to learn something well is to try it by yourself. Since to see code examples can give you a great help, HaTeX comes with several examples where you can see by yourself how to get the work done.
The API reference is also a good point to keep in mind. Descriptions of functions make you know how exactly they works. And, when they are not present, function names with type signatures may be very helpful and descriptive.
3 LaTeX blocks and the Writer monad
===The Writer Monad=== Fixed a monoiddata W m a = W m a
inject :: Monoid m => a -> W m a inject a = W mempty a
instance Functor (W m) where fmap f (W m a) = W m (f a)
join :: Monoid m => W m (W m a) -> W m a join (W m (W m' a)) = W (mappend m m') a
instance Monoid m => Monad (W m) wherereturn = injectw >>= f = join (fmap f w)
tell :: m -> W m () tell m = W m ()
execute :: W m a -> m execute (W m a) = m
instance Monoid Int wheremempty = 0 mappend = (+)example :: Int example = execute $ do
tell 1 tell 2 tell 3tell 4
example :: Int example = execute $ mapM_ tell [ 1 .. 4 ]
3.1 The LaTeX Monad
Let's go back to thetype LaTeXW = W LaTeX
example :: LaTeXexample = execute $ do
tell $ documentclass [] article tell $ author "Monads lover"tell $ title "LaTeX and the Writer Monad"
author' :: LaTeXW a -> LaTeXW () author' = tell . author . execute
example :: LaTeXexample = execute $ do
documentclass' [] article author' "Monads lover"title' "LaTeX and the Writer Monad"
3.2 Composing monads
To add flexibility to HaTeX, the writer monad explained above is defined as a monad transformer, namedfoo :: Monad m => LaTeXT m a
type LaTeXW = LaTeXT Mfoo :: LaTeXW a
type LaTeXIO = LaTeXT IO readCode :: FilePath -> LaTeXIO () readCode fp = lift (readFileTex fp) >>= verbatim . raw example :: LaTeXIO () example = do "This is the code I wrote this morning:" readCode "foo.hs" "It was a funny exercise."
type LaTeXW = LaTeXT Identity
4 The LaTeXC class
HaTeX has two different interfaces. One uses blocks as5 Packages
LaTeX, in addition to its predefined commands, has a big number of packages that increase its power. HaTeX functions for some of these packages are defined in separated modules, one module per package. This way, you can import only those functions you actually need. Some of these modules are below explained.
5.1 Inputenc
This package is of vital importance if you use non-ASCII characters in your document. For example, if my name is Ángela, the Á character will not appear correctly in the output. To solve this problem, use theimport Text.LaTeX.Base import Text.LaTeX.Packages.Inputenc thePreamble :: LaTeX thePreamble = documentclass [] article <> usepackage [utf8] inputenc <> author "Ángela" <> title "Issues with non-ASCII characters"
5.2 Graphicx
With theincludegraphics :: LaTeXC l => [IGOption] -> FilePath -> l
6 Epilogue
6.1 Notes about this guide
This guide is not static. It will be changed, extended and improved with the time. If you think there is something unclear, something hard to understand, please, report it.
6.2 Notes from the author
It has been a long time since I started with the HaTeX project. But it was not until version 3.3 that I feel like it is taking the right way. This is why I did not write this guide before.
I learned a lot during its creation, beginning with a little project that would help myself to write LaTeX documents, and then becoming in my first contact with the Haskell Community. And all the time it was funny.
I would like to end this guide saying thanks to all people that has been interested in HaTeX some way, specially to those that contributed to it with patches, opinions or bug reports. Thanks.
7 Footnotes
1: With well-formed we mean that all braces, environments, math expressions, ... are closed.
2: Please, note that theis a Haskell value, not the LaTeX code itself.
3: From GHC 7.4,versions of GHC, HaTeX exports the synonym.
4: Some authors write it using tuples, like this:6: In fact, we had a problem with HaTeX-meta, the program that automatically generated the duplicated functions. The problem was described in a blog post: http://deltadiaz.blogspot.com.es/2012/04/hatex-trees-and-problems.html.
