# Compile .tex and remove .div file from Haskell

As a practice for interacting with system commands, I wrote a Haskell program that compiles a .tex file specified by the argument.

When one execute this program:

• If no argument is specified, print "Specify a file!" then exit
• If the specified file does not exist, print "File not found." then exit
• If the specified file does not have the extension .tex, then print "Not a .tex file!" then exit
• If a .tex file is specified (report.tex), execute the following commands
• $uplatex report.tex -o report.dvi • $ dvipdfmx report.dvi
• $rm report.dvi • The program does not have to handle errors in the execution of tex-related commands. This is what I wrote: import qualified Control.Shell as S import Data.Char (toLower) import Data.List (dropWhileEnd) import qualified System.Environment as E main = do args <- E.getArgs case args of [] -> putStrLn "Specify a file!" (filename:_) -> handler filename isTeXFile :: S.FilePath -> Bool isTeXFile = ( == ".tex") . map toLower . S.takeExtension handler :: S.FilePath -> IO () handler f = do (Right isfile) <- S.shell$ S.isFile f
if not isfile
else
if isTeXFile f
then do
result <- compile f
case result of
(Left l) -> putStrLn l
(Right _) -> return ()
else
putStrLn "Not a .tex file!"

compile :: S.FilePath -> IO (Either String ())
compile f = S.shell $do S.run_ "uplatex" [f, "-o", dvifile] "" S.run_ "dvipdfmx" [dvifile] "" S.rm dvifile where basename = dropWhileEnd (/= '.') f dvifile = basename ++ "dvi"  I appreciate any kind of feedback. I'm especially concerned about: • The body of handler seems unnecessarily complicated. How can I clean it up? • Am I using Control.Shell in the right way? Is there other function I should use instead? Maybe I should consider using System.Process? Side note: I don't use pdflatex because it is not as good as uplatex at Japanese typesetting and I haven't been catching up with the latest LaTeX families. • Welcome to Code Review! That's an interesting question, I hope you get some good reviews! – Phrancis Dec 26 '14 at 15:05 ## 1 Answer When you are using packages, please make sure to say which ones. I guessed shellmate btw. As for the code, it's quite readable except for the handler definition which I've rewritten as follows: handler :: S.FilePath -> IO () handler f = do (Right isfile) <- S.shell$ S.isFile f
else if not $isTeXFile f then putStrLn "Not a .tex file!" else compile f >>= either putStrLn return  Given that you are quickly dismissing the cases you are not interested in, I inspected not$ isTeXFile f and systematically written the if (...) then clauses on the same line. This avoids the crazy right-leaning nesting.

Using the either combinator of type (a -> c) -> (b -> c) -> Either a b -> c lets you rewrite the final case expression in a more concise fashion.

Finally, I don't think it's good practice to test a file's type based on its extension. Given that you are already running external tools, you may want to use ̀file for that job. It would look something like this:

isTeXFile :: S.FilePath -> IO Bool
isTeXFile fp = do
(Right ans) <- S.shell $S.run "file" [fp] "" return$ " LaTeX " isInfixOf ans

handler :: S.FilePath -> IO ()
handler f = do
(Right isfile) <- S.shell $S.isFile f if not isfile then putStrLn "File not found." else do testTexFile <- isTeXFile f if not$ testTexFile then putStrLn "Not a .tex file!"
else compile f >>= either putStrLn return


The rest is pretty much perfect as far as I'm concerned (modulo some cosmetic preferences which I won't bore you with).

• Thank you very much! Your guess is right, the package I used was shellmate (I had forgotten that I'd installed it with cabal!). – Yosh Dec 29 '14 at 5:35