
# 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__  <br/>
>    1.1 The Markdown block rule <br/>
>    1.2 The Markdown line rule  <br/>
>    1.3 The literal block rules <br/>
>    1.4 Final remarks on the Markdown syntax   <br/>
>  __2. The converter functions and document generation__   <br/>
>    2.1 The converters <br/>
>    2.2 The default function call and the HaskellDown manual<br/>
>  __Appendix A. Implementation__ <br/>
>  __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

    <h3><strong>Haskell</strong> properties</h3>
    <ul>
      <li>purely functional</li>
      <li>strongly typed</li>
      <li>really lazy</li>
    </ul>

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

    <h1>A new chapter in <em>literal programming</em></h1>

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

    <pre><code>triple :: Int -&gt; Int
    triple n = 3 * n
    </code></pre>

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:

    <html>
      <head>
        <title>The HaskellDown Manual</title>
        <style type="text/css">  ... content of HaskellDown.css ... </style>
      </head>
      <body>
        ... content of HaskellDown.hs, converted to HTML ...
      </body>
    </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 =
        "<html>\n" ++
        "<head>\n" ++
        "<title>" ++ title ++ "</title>\n" ++
        "<style type=\"text/css\">\n" ++ css ++ "</style>" ++
        "</head>\n" ++
        "<body>\n" ++ htmlBody ++ "</body>\n" ++
        "</html>\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"
