# HaskellDown __HaskellDown__ is a simple method to generate HTML documents from [Haskell][] files, by using the plain [Markdown][] text formatting syntax. `HaskellDown.hs` is the Haskell module that provides the functions to conveniently perform these conversions. HaskellDown is thus similar to the standard Haskell documentation tool [Haddock][], but uses a different approach. __Table of contents__ > __1. HaskellDown syntax__
> 1.1 The Markdown block rule
> 1.2 The Markdown line rule
> 1.3 The literal block rules
> 1.4 Final remarks on the Markdown syntax
> __2. The converter functions and document generation__
> 2.1 The converters
> 2.2 The default function call and the HaskellDown manual
> __Appendix A. Implementation__
> __Appendix B. References__ ## 1. HaskellDown syntax __HakellDown__ comprises just three simple syntax rules that modify ordinary Haskell comments so that they become text parts that will be converted into first Markdown and then HTML. ### 1.1 The Markdown block rule > A __Markdown block__ has the form > > {--- > ... this is the Markdown text part ... > ---} > > It starts after a single line beginning with `{---` and ends before a single line that begins with `---}`. For example, this Hakell comment with Markdown code {--- ### __Haskell__ properties * purely functional * strongly typed * really lazy ---} will turn into

Haskell properties

Note, that the comment delimiters `{---` and `---}` both have to be at the beginning of a line. ### 1.2 The Markdown line rule > A __Markdown line__ has the form > > -- -- ... this is the Markdown text part ... > > Everything after `-- -- ` (i.e. two dashes, one space, two dashes, one space) is considered Markdown and > converted accordingly. Note, the delimiter `-- -- ` has to be at the beginning of a line and that the Markdown really starts after the last space symbol, not directly after the last dash. For example, a line beginning with -- -- # A new chapter in _literal programming_ will turn into

A new chapter in literal programming

But this line -- --# A new chapter in _literal programming_ will be ignored and not recognized as a Markup line. ### 1.3 The literal block rule > A __literal block__ has the form > > -- -- -- > ... Haskell source code ... > -- -- -- > > The beginning and end of a literal block is indicated by a line that starts with `-- -- --` (i.e. three double > slashes, separated by a space, each). > Everything between these delimiter lines is considered literal Haskell code and will be displayed as such. For example, -- -- -- triple :: Int -> Int triple n = 3 * n -- -- -- will after the conversion to Markdown be turned into the same block, but all lines indented by four spaces triple :: Int -> Int triple n = 3 * n and that will be translated into HTML as
triple :: Int -> Int
    triple n = 3 * n
Note, that everything else in a line that starts with `-- -- --` is cut off. This means, we can use additional comments to help structuring literal blocks. We could have written our previous example as -- -- -- START OF LITERAL BLOCK triple :: Int -> Int triple n = 3 * n -- -- -- FINISH OF LITERAL BLOCK and would still have obtained the same results. Also note, that with this rule, we can not only expose the type signature of a function, as in [Haddock][], like so: -- -- -- triple :: Int -> Int -- -- -- triple n = 3 * n but we can also choose to automatically put the whole function definition into the documentation (as one of the key ideas in _Literal Programming_). ### 1.4 Final remarks on the HaskellDown syntax You should not try to nest these rules in any way. For example, don't put a literal block inside a Markdown block. Everything else, i.e. all code of the Haskell source, which is neither in a Markdown block, or on a Markdown line, or inside a literal block, is ignored by HaskellDown and will not appear after the conversion. ## 2. The converter functions and document generation ### 2.1 The converters The main converter has the following type haskellToHtml Haskell ---------------------------------------------------------------> Html and that is the composition of two converters haskellToMarkdown markdownToHtml Haskell --------------------------> Markdown --------------------------> Html A call of `haskellToMarkdown` takes the Haskell source code, extracts the Markdown parts from the `{--- ... ---}` and `-- -- ` comment and indents the literal blocks to suit the Markdown syntax for code blocks; all that as described in part 1. The `markdownToHtml` then does the conversion as described in the [Markdown][] standard. I used the implementation provided by the [Pandoc][] module, which is part of the Haskell Platform. In these type signatures `Haskell`, `Markdown`, and `Html` are type synonyms for `String`s. But there are also versions, that work on files (i.e. file names), containing the `Haskell`, `Markdown`, and `Html` code, respectively. In each of the functions, the first argument is the source code file and the second argument is the (name of the) target file: haskellToHtmlFile :: HaskellFile -> HtmlFile -> IO () haskellToMarkdownFile :: HaskellFile -> MarkdownFile -> IO () markdownToHtmlFile :: MarkdownFile -> HtmlFile -> IO () For example, we can apply that to this `HaskellDown.hs` file. Start the `ghci`, load the module Prelude> :l HaskellDown.hs and call *HaskellDown> haskellToHtmlFile "HaskellDown.hs" "HaskellDownManual.html" for the generation of the HTML file `HaskellDownManual.html` and *HaskellDown> haskellToMarkdownFile "HaskellDown.hs" "HaskellDownManual.markdown" if you rather need the Markdown version in `HaskellDownManual.markdown`. ### 2.2 The default function call and the HaskellDown manual `haskellToHtml` (or `haskellToHtmlFile`) is indeed the core conversion. But next to this pure HTML result, it would be nice for most practical purposes to have some more options. In a compromise of flexibility and simplicity, I choose two more standard arguments: a HTML document `title` and a `cssFile`. So our name and type for the default converter is: haskellDown :: HaskellFile -> String -> CssFile -> HtmlFile -> IO () For example, if `HaskellDown.css` is the name of a default CSS file, we can generate a nicely displayed document called `HaskellDownManual.html` of this given `HaskellDown.hs` source file by calling haskellDown "HaskellDown.hs" "The HaskellDown Manual" "HaskellDown.css" "HaskellDownManual.html" Afterwards, the content of `HaskellDownManual.html` will be something like this: The HaskellDown Manual ... content of HaskellDown.hs, converted to HTML ... To produce the same result file `HaskellDownManual.html`, we actually provide another function, that does exactly that: makeHaskellManual :: IO () ## Appendix A. Implementation ### A.1 The exports of the `HaskellDown` module module HaskellDown ( -- * Type synonyms Haskell, Markdown, Html, Css, HaskellFile, MarkdownFile, HtmlFile, CssFile, -- * The converter functions haskellToMarkdown, markdownToHtml, haskellToHtml, haskellToMarkdownFile, markdownToHtmlFile, haskellToHtmlFile, -- * The default function call and the HaskellDown manual haskellDown, makeHaskellDownManual, ) where ### A.2 The imports import Text.Pandoc (writeHtmlString, defaultWriterOptions, readMarkdown, defaultParserState) ### A.3 Type synonyms type Haskell = String type Markdown = String type Html = String type Css = String type HaskellFile = FilePath type MarkdownFile = FilePath type HtmlFile = FilePath type CssFile = FilePath ### A.4 The converters data Mode = HASKELL | MARKDOWN | LITERAL -- only for use in the following function haskellToMarkdown :: Haskell -> Markdown haskellToMarkdown = unlines . (iter HASKELL) . lines where iter :: Mode -> [Haskell] -> [Markdown] iter HASKELL [] = [] iter _ [] = error "Source code did not terminate in proper HASKELL mode!" iter HASKELL (row:rows) = if (take 4 row) == "{---" then (drop 4 row) : (iter MARKDOWN rows) else if (take 8 row) == "-- -- --" then "" : (iter LITERAL rows) else if (take 6 row) == "-- -- " then (drop 6 row) : (iter HASKELL rows) else iter HASKELL rows iter MARKDOWN (row:rows) = if (take 4 row) == "---}" then iter HASKELL rows else row : (iter MARKDOWN rows) iter LITERAL (row:rows) = if (take 8 row) == "-- -- --" then "" : (iter HASKELL rows) else (" " ++ row) : (iter LITERAL rows) markdownToHtml :: Markdown -> Html markdownToHtml = writeHtmlString defaultWriterOptions . readMarkdown defaultParserState haskellToHtml :: Haskell -> Html haskellToHtml = markdownToHtml . haskellToMarkdown haskellToMarkdownFile :: HaskellFile -> MarkdownFile -> IO () haskellToMarkdownFile haskellSourceFile markdownTargetFile = do haskell <- readFile haskellSourceFile let markdown = haskellToMarkdown haskell writeFile markdownTargetFile markdown markdownToHtmlFile :: MarkdownFile -> HtmlFile -> IO () markdownToHtmlFile markdownSourceFile htmlTargetFile = do markdown <- readFile markdownSourceFile let html = markdownToHtml markdown writeFile htmlTargetFile html haskellToHtmlFile :: HaskellFile -> HtmlFile -> IO () haskellToHtmlFile haskellSourceFile htmlTargetFile = do haskell <- readFile haskellSourceFile let html = haskellToHtml haskell writeFile htmlTargetFile html ### A.5 Document generation and the HaskellDown Manual htmlDocument :: String -> Css -> Html -> Html htmlDocument title css htmlBody = "\n" ++ "\n" ++ "" ++ title ++ "\n" ++ "" ++ "\n" ++ "\n" ++ htmlBody ++ "\n" ++ "\n" defaultCssFile :: CssFile defaultCssFile = "HaskellDown.css" haskellDown :: HaskellFile -> String -> CssFile -> HtmlFile -> IO () haskellDown haskellSourceFile title cssFile htmlTargetFile = do haskell <- readFile haskellSourceFile css <- if null cssFile then return "" else readFile cssFile let htmlBody = haskellToHtml haskell let doc = htmlDocument title css htmlBody writeFile htmlTargetFile doc haskellDownManual :: HtmlFile haskellDownManual = "HaskellDownManual.html" makeHaskellDownManual :: IO () makeHaskellDownManual = haskellDown "HaskellDown.hs" "The HaskellDown Manual" defaultCssFile "HaskellDownManual.html" ## Appendix B. References * [CodeDown][] home page for CodeDown in general and HaskellDown in particular * [Haskell][] home page * [Haddock][] - the standard documentation tool for Haskell * [Syntax and conversion tools for Markdown][Markdown] * [Pandoc][] - the Haskell library, here used for Markdown-to-HTML conversion [CodeDown]: http://www.bucephalus.org/CodeDown "CodeDown" [Haskell]: http://www.haskell.org "home page of the Haskell programming language" [Markdown]: http://daringfireball.net/projects/markdown "primary source of Markdown, written by John Gruber" [Haddock]: http://www.haskell.org/haddock "the standard Haskell documentation tool" [Pandoc]: http://johnmacfarlane.net/pandoc/ "Pandoc - a universal document converter by John MacFarlane"