shall be the primary address for more information.
-}
{---
# 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
- purely functional
- strongly typed
- really lazy
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"
---}