HaskinTeX

A program to evaluate Haskell code within LaTeX

Daniel Díaz

The haskintex program

The haskintex program is a tool that reads a LaTeX file and evaluates Haskell expressions contained in some specific commands and environments. It allows you to define your own functions, use any GHC Haskell language extension and, in brief, anything you can do within Haskell. You can freely add any Haskell code you need, and make this code appear optionally in the LaTeX output. It is a tiny program, and therefore, easy to understand, use and predict.

Installation

To install haskintex from Hackage, use the cabal program:

? cabal update ? cabal install haskintex

This will update your package database and install the latest version of haskintex. Otherwise, you can build the current developing version downloading its code as a zip file.

Note that this page will be updated with every new version released to Hackage, so it reflects the features of the last released version. If you have a previous version, some of the features explained here may not apply to you. To check what version you have installed, run haskintex -help .

How does it work?

Consider the following simple example.

\documentclass{article} \begin{document} I have \evalhaskell{2+3} fingers in my right hand. \end{document}

If the above LaTeX code is written in foo.htex , after running haskintex foo.htex the file foo.tex will be written with:

\documentclass{article} \begin{document} I have \verb`5` fingers in my right hand. \end{document}

The Haskell code 2+3 has been evaluated to 5 , and the result has been enclosed in a verb command. The evaluation is done using GHC, so it must be installed in order to use haskintex.

The file foo.htex can be processed running hakintex foo as well. When the given input file does not exist, haskintex adds the .htex extension automatically. If the file with the added extension does not exist, it throws an error.

Let's go through another example. The factorial function can be implemented recursively in Haskell.

fact :: Int -> Int fact 0 = 1 fact n = n * fact (n-1)

To include this function in the scope of evalhaskell use writehaskell as shown below.

\documentclass{article} \begin{document} \begin{writehaskell} fact :: Int -> Int fact 0 = 1 fact n = n * fact (n-1) \end{writehaskell} While 5 factorial is \evalhaskell{fact 5}, 10 factorial is \evalhaskell{fact 10}. \end{document}

Therefore, to add binding definitions use the writehaskell environment, and to evaluate code use evalhaskell . If the output of an evaluation is known to be big and it is not desired to be inlined, use evalhaskell as an environment instead. For example:

\documentclass{article} \begin{document} This is the list of numbers from 1 to 1000: \begin{evalhaskell} [1..1000] \end{evalhaskell} \end{document}

Lines in the output of evalhaskell as environment are bounded to 60 characters. A line break will be introduced if this number is exceeded.

Using HaTeX

It is possible to use any Haskell type within the evalhaskell command or environment. Whatever the result is, it will be added to the LaTeX output using the Show instance given by the type of the input expression. Sometimes, this is good enough, but probably you will end up wanting a more complex way to display your results. To approach this problem, there is a library called HaTeX which defines a LaTeX type. Using this library, you can define your own functions from and to this type. In the other hand, haskintex lets you use the command hatex . This command receives as input a Haskell expression of type LaTeX and generates its corresponding LaTeX code as output. It is recommended to take a look at the HaTeX library to make yourself an idea of the possibilities you have. As an example, this is how you draw a portion of a logarithmic spiral.

\documentclass{article} \usepackage{tikz} \usepackage[utf8]{inputenc} \author{Daniel Díaz} \title{Embedding HaTeX in \emph{haskintex}} \begin{document} \maketitle Below is the \emph{Spira Mirabilis} inserted using the HaTeX package. \begin{writehaskell} import Text.LaTeX import Text.LaTeX.Packages.TikZ.Simple spiral :: Figure spiral = LineWidth (Pt 2) $ pathImage 0.01 (0,4) $ \t -> ( a * exp t * cos (b*t) , a * exp t * sin (b*t) ) where a = 0.1 ; b = 4 \end{writehaskell} \hatex{center $ tikzpicture $ figuretikz spiral} \end{document}

The output looks like this:

The example above uses functions from the Text.LaTeX.Packages.TikZ.Simple module, which deals with graphic generation using TikZ scripts. You may take a look to its API documentation to fully understand the given example.

Using HaTeX with IO

While the hatex command opens tons of possibilities to generate LaTeX code, it suffers from an unfortunate restriction. Since it only accepts expressions of type LaTeX , it will never accept an expression that performs IO computations. To solve this, use the iohatex command. It works the same way as the hatex command does, but it expects an expression of type IO LaTeX instead. Here an example:

\documentclass{article} \begin{document} \begin{writehaskell} import Text.LaTeX import Data.Time (getCurrentTime) import Control.Applicative (()) import Data.String (fromString) \end{writehaskell} I am testing the \texttt{iohatex} command when the time is \iohatex{fromString . show getCurrentTime}. \end{document}

Including Pragmas

If you include pragmas with the writehaskell environment and run haskintex with the keep flag, you will notice that the pragma is included below the module ... where clause, and therefore it is not working. To include pragmas, use the haskellpragmas environment. It would go like this:

\documentclass{article} \begin{document} \begin{haskellpragmas} {-# LANGUAGE OverloadedStrings #-} \end{haskellpragmas} \begin{writehaskell} import Text.LaTeX overloaded :: LaTeX overloaded = "This is an Overloaded String." \end{writehaskell} \hatex{overloaded} \end{document}

Code ordering

The place where evalhaskell is called does not really matter. The function fact can be called before defining it.

\documentclass{article} \begin{document} While 5 factorial is \evalhaskell{fact 5}, 10 factorial is \evalhaskell{fact 10}. \begin{writehaskell} fact :: Int -> Int fact 0 = 1 fact n = n * fact (n-1) \end{writehaskell} \end{document}

Note that this also applies to pragmas. If you include a pragma at the end (see below), it will still apply to the Haskell code.

\documentclass{article} \begin{document} \begin{writehaskell} import Text.LaTeX overloaded :: LaTeX overloaded = "This is an Overloaded String." \end{writehaskell} \hatex{overloaded} \begin{haskellpragmas} {-# LANGUAGE OverloadedStrings #-} \end{haskellpragmas} \end{document}

What haskintex does is to traverse the LaTeX code twice. The first time it creates a Haskell module with all the code defined with writehaskell and haskellpragmas environments, placing the pragmas at the beginning of the file. In the second pass, it evaluates all the evalhaskell calls with the created module imported. Note that this means you have to avoid duplicated names.

Visibility

By default, the Haskell code introduced by writehaskell (or haskellpragmas ) will not show up in the processed LaTeX code. To change this, use the visible option. For example:

\documentclass{article} \begin{document} \begin{writehaskell}[visible] fact :: Int -> Int fact 0 = 1 fact n = n * fact (n-1) \end{writehaskell} While 5 factorial is \evalhaskell{fact 5}, 10 factorial is \evalhaskell{fact 10}. \end{document}

A verbatim environment will contain the code. To change the default behavior and make Haskell code visible by default, pass the -visible flag when invoking haskintex . Use then the hidden option to hide specific calls to writehaskell (or haskellpragmas ).

\documentclass{article} \begin{document} \begin{writehaskell}[hidden] fact :: Int -> Int fact 0 = 1 fact n = n * fact (n-1) \end{writehaskell} While 5 factorial is \evalhaskell{fact 5}, 10 factorial is \evalhaskell{fact 10}. \end{document}

You can still use the visible option but it will be pretty much useless. However, it may be useful if you are switching the -visible flag on and off frequently.

Memorization

haskintex

memo option . You can add this option to hatex , iohatex , and evalhaskell commands and environments. When the enclosed expression is evaluated, its result will be stored in the memo tree (the structure haskintex uses internally to store pairs of unevaluated code and its result). The next time the same expression is found, if it is marked with memo as well, it won't be evaluated again: the stored result will be used instead.

. You can add this option to , , and commands and environments. When the enclosed expression is evaluated, its result will be stored in the memo tree (the structure uses internally to store pairs of unevaluated code and its result). The next time the same expression is found, if it is marked with as well, it won't be evaluated again: the stored result will be used instead. notmemo option . This option can be applied in the same places than memo . It will force a given Haskell expression to be evaluated every time, not using the memo tree.

. This option can be applied in the same places than . It will force a given Haskell expression to be evaluated every time, not using the memo tree. memo flag . This flag applies the memo option everywhere it can be applied, except for those places where the notmemo option has been specified.

. This flag applies the option everywhere it can be applied, except for those places where the option has been specified. memoclean flag. This flag causes haskintex to remove the entire memo tree at the end of its execution. This means that if several files are processed in a single haskintex call, the memo tree will be removed after processing all the files.

To sum up: place the memo option to those expressions whose value don't change over time and you don't want to compute twice. And let haskintex do the rest. Alternatively, use the memo flag and apply the notmemo option to those expressions whose value may change over time.

Sandboxes

If haskintex is running inside a cabal sandbox environment, it will use the sandbox package db when evaluating Haskell code. If you don't want this to happen, use the -nosandbox flag and the sandbox package db will be ignored.

Flag reference

Below a list of the flags accepted by the program. Any argument starting with - will be recognized as a flag, even if it is not defined (in which case, it will have no effect). Anything else will be considered an input file. For instance, this is a valid invocation to process the file foo.htex verbosely: haskintex foo -verbose .

autotexy : Apply the function texy from the Text.LaTeX.Base.Texy module of the HaTeX package to every expression in a hatex or iohatex command. This effectively allows the user to write expressions in types other than LaTeX and have haskintex to perform the required transformation.

: Apply the function from the Text.LaTeX.Base.Texy module of the HaTeX package to every expression in a or command. This effectively allows the user to write expressions in types other than and have to perform the required transformation. debug : Only for debugging purposes. It writes a file with extension .debughtex with the AST of the internal representation of the input file haskintex uses.

: Only for debugging purposes. It writes a file with extension .debughtex with the AST of the internal representation of the input file uses. help : Show the version of the program and some information, like usage and a description of the different flags.

: Show the version of the program and some information, like usage and a description of the different flags. keep : Do not remove the auxiliary module after the program ends. The name of the auxiliary module is the name of the input file preceded by Haskintex_ .

: Do not remove the auxiliary module after the program ends. The name of the auxiliary module is the name of the input file preceded by . lhs2tex : By default, haskintex uses basic LaTeX verb and verbatim declarations for Haskell code. When this flag is enabled, the output will be formatted for lhs2TeX.

: By default, uses basic LaTeX and declarations for Haskell code. When this flag is enabled, the output will be formatted for lhs2TeX. manual : This flag will make haskintex write evalhaskell and writehaskell outputs unchanged. In other words, no verb , verbatim , or code (in case the lhs2tex flag is enabled) declarations will be used.

: This flag will make write and outputs unchanged. In other words, no , , or (in case the lhs2tex flag is enabled) declarations will be used. memo : Unless otherwise specified, every evalhaskell , hatex or iohatex command (or environment) will be called with the memo option.

: Unless otherwise specified, every , or command (or environment) will be called with the option. memoclean : Cleans the memo tree after the execution of haskintex . If several files are processed, the memo tree will be cleaned after processing all of them. Read more about the memo tree in the Memorization section.

: Cleans the memo tree after the execution of . If several files are processed, the memo tree will be cleaned processing all of them. Read more about the memo tree in the section. nosandbox : Do not use the sandbox package db even if haskintex is running in one.

: Do not use the sandbox package db even if is running in one. overwrite : Overwrite the output file if it already exists. If this flag is not set, the program will ask before overwriting.

: Overwrite the output file if it already exists. If this flag is not set, the program will ask before overwriting. stdout : Instead of writing the final output to a file, send it to the standard output stream (usually, the screen), making possible to redirect the output to another application.

: Instead of writing the final output to a file, send it to the standard output stream (usually, the screen), making possible to redirect the output to another application. verbose : While working, print information on the screen about the execution.

: While working, print information on the screen about the execution. visible : Make Haskell code in writehaskell environments visible by default.

Code repository

The code of haskintex is hosted on GitHub. For suggestions, bug reports, or any other concern, fill an issue at the Issue Tracker.