Haskell Libraries : Html Library Documentation

Here are some notes on using the HTML libraries. This is rather sketchy right now; hopefully it will improve over time.

# A First Example

As a first example, lets print the following page.

Haskell |
A Purely Functional Language

Haskell is a general purpose, purely functional programming language.

First, you just import the module `Html` to use these combinators.

`import Html`

We define our page as:

`htmlPage` `:: [``Html``]` `htmlPage` `` `=` `header` `` `<<` `thetitle` `` `<<` `"My Haskell Home Page"` `` `+++` `body` `!` `[``bgcolor` `"#aaff88"``]` `<<` `theBody`

The definition of htmlPage reads: First we have a header, which contains a title, which contains the text "My Haskell Home Page". After this, we have a body, with attribute bgcolor set to #aaff88, and this body contains the Html defined by theBody. Don't worry about the type of things right now, just try get a feel for what the combinators look like.

`theBody` `:: [``Html``]` `theBody` `=` `` `table` `!` `[``border 0``]` `<<` `tableContents` `` `+++` `br` `` `+++` `p` `<<` `message` `message` `` `=` `"Haskell is a general purpose, purely functional programming language."`

This reads: the body is a table (with a border), the contents of the table are defined by `tableContents`. This is followed by a br (an explicit line break), and a paragraph containing a message.

Now need to define the tableContents. For this we use our special table combinators.

`tableContents` `::` `HtmlTable` `tableContents` `= (``haskell `above` purely``)` ``beside` lambda` `` `where` `` `haskell` `=` `td` `!` `[``align` `"center"``]` `` `<<` `font` `!``[``size` `"7"``,``face` `"Arial Black"``]` `` `<<` `"Haskell"` `` `purely` `=` `td` `<<` `font` `!` `[``size` `"6"``]` `` `<<` `"A Purely Functional Language"` `` `lambda` `=` `td` `<<` `image` `!` `[``src` `"lambda.gif"``]`

This produces a table of cells, which nest like this:

haskell | lambda
purely

Even though the lambda box sits over two rows, the semantics of above and beside handle this correctly.

Now we can render our HTML page.

`main` `=` `writeFile` `"example.htm"` `(``renderHtml htmlPage``)`

The whole page (put in a box) looks like this:

Haskell |
A Purely Functional Language
Haskell is a general purpose, purely functional programming language.

The raw Html produce by these macros is:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 FINAL//EN">
    <!--Rendered using the Haskell Html Library v0.1-->
    <HTML
    ><HEAD
      ><TITLE
        >My Haskell Home Page</TITLE
        ></HEAD
      ><BODY BGCOLOR = "#aaff88"
      ><TABLE BORDER = "1"
        ><TR
          ><TD ALIGN = "center"
    	><FONT SIZE = "7" FACE = "Arial Black"
    	  >Haskell</FONT
    	  ></TD
    	><TD ROWSPAN = "2"
    	><IMG SRC = "lambda.gif"
    	  ></TD
    	></TR
          ><TR
          ><TD
    	><FONT SIZE = "6"
    	  >A Purely Functional Language</FONT
    	  ></TD
    	></TR
          ></TABLE
        ><BR
        ><P
        >Haskell is a general purpose, purely functional programming language.</P
        ></BODY
      ></HTML
    >

This might look strange, but in HTML, space matters, and this layout style (borrowed from the York XML team) is used to avoid extra spaces.

# Domain Specific Combinators for HTML

Lets look at the type of some combinators.

`header` `:: [``Html``] ->` `Html` `thetitle` `:: [``Html``] ->` `Html` `body` `:: [``Html``] ->` `Html` `stringToHtml` `::` `String` `->` `Html`

These all take a list of Html (the Html inside the specific constructor). Ignoring the bgcolor argument for now, we could write htmlPage as:

`htmlPage` `:: [``Html``]` `htmlPage` `=` `` `[``header` `[``thetitle` `[``stringToHtml` `"My Haskell Home Page"``]],` `` `body theBody``]`

Some simple combinators, however, make things much clearer.

`class``` `MARKUP a` `where``` `{` `markup` `::` `a` `-> [``Html``] }` `infixr``` `7` `<<` `(``<<``) :: (``MARKUP a``) => ([``Html``] ->` `b``) ->` `a` `->` `b` `fn` `<<` `arg` `=` `fn` `(``markup arg``)`

This nesting takes a function that maps a list of Html to something, and inserts a conversion function (called markup) round the second argument, then completes the application.

MARKUP is overloaded at Html, [Html], and String, meaning that any of them are a valid second argument to <<.

So we can write:

`htmlPage` `:: [``Html``]` `htmlPage` `=` `` `[``header` `` `<<` `thetitle` `` `<<` `"My Haskell Home Page"``,` `` `body` `<<` `theBody``]`

Which expresses the nesting in a clear way. Now we need a way of appending two MARKUP objects:

`infixr``` `2` `+++` `-- combining Html` `(``+++``) :: (``MARKUP a``,``MARKUP b``) =>` `a` `->` `b` `-> [``Html``]` `a` `+++` `b` `=` `concat` `[``markup a``,``markup b``]`

So we can now write:

`htmlPage` `:: [``Html``]` `htmlPage` `=` `` `header` `` `<<` `thetitle` `` `<<` `"My Haskell Home Page"``,` `` `+++` `body` `<<` `theBody`

Now we need to be able to add arguments.

`infixl 8` `!` `-- adding optional arguments` `class``` `ADDATTRS a` `where` `` `(``!``) ::` `a` `-> [``HtmlAttr``] ->` `a`

This says that we can use ! to add arguments. ADDATTRS is overloaded for (a -> Html) and Html. (We actually jump through a few hoops here to allow Haskell98 complience). So ...

`(``body` `!` `[``bgcolor` `"orange"``]) :: [``Html``] ->` `Html`

... but the body also has the attribute, bgcolor "orange". So we can now say:

`htmlPage` `:: [``Html``]` `htmlPage` `=` `` `header` `` `<<` `thetitle` `` `<<` `"My Haskell Home Page"``,` `` `+++` `body` `!` `[` `bgcolor` `"orange"``]` `<<` `theBody`

This library provides many specific functions like header and thetitle, as well as attribute builders, like bgcolor. Look at the source for more details.

# Building Tables

We provide the following to help build tables.

`cell` `:: (``HTMLTABLE ht``) =>` `ht` `->` `HtmlTable` `(``</>``),``above``,(``<->``),``beside` `` `:: (``HTMLTABLE ht1``,``HTMLTABLE ht2``) =>` `ht1` `->` `ht2` `->` `HtmlTable` `aboves``,``besides` `` `:: (``HTMLTABLE ht``) => [``ht``] ->` `HtmlTable`

HTML has instances for both Html and HtmlTable. cell can be used to wrap up individual elements as (small) tables. above, aboves, etc, can be used to construct larger tables. In many cases, the overloading allows the cell construct to be omitted.

`tableContents` `= (``haskell `above` purely``)` ``beside` lambda` `` `haskell` `=` `td` `!` `[``align` `"center"``]` `` `<<` `font` `!``[``size` `"7"``,``face` `"Arial Black"``]` `` `<<` `"Haskell"` `` `purely` `=` `td` `<<` `font` `!` `[``size` `"6"``]` `` `<<` `"A Purely Functional Language"` `` `lambda` `=` `td` `<<` `image` `!` `[``src` `"lambda.gif"``]`

Here we have implicit cell's being inserted because we are using above and beside.

To get a table contents from a HtmlTable, just use markup, or its DSL sibling, <<, because HtmlTable is also an instance of MARKUP.

`example` `::` `Html` `example` `=` `table` `!` `[``border 0``]` `<<` `tableContents`

# Debugging and Displaying Nested Structures

Consider if we now want to debug the above example.

`example` `::` `Html` `example` `=` `table` `!` `[``border 0``]` `<<` `tableContents`

How can we do this? Html is an instance of Show, giving.

    <TABLE BORDER = "0">
       <TR>
          <TD ALIGN = "center">
             <FONT SIZE = "7" FACE = "Arial Black">
                Haskell
             </FONT>
          </TD>
          <TD ROWSPAN = "2">
             <IMG SRC = "lambda.gif">
          </TD>
       </TR>
       <TR>
          <TD>
             <FONT SIZE = "6">
                A Purely Functional Language
             </FONT>
          </TD>
       </TR>
    </TABLE>

However, we can do better. This is pure ASCII. Why not use HTML to debug HTML?

`example2` `::` `Html` `example2` `=` `debugHtml example`

Rendering example2 gives:

Debugging Output
<TABLE BORDER="0">
| <TR >
| <TD ALIGN="center">
| <FONT SIZE="7" FACE="Arial Black">
| Haskell
</FONT>
</TD>
<TD ROWSPAN="2">
| <IMG SRC="lambda.gif">
</TD>
</TR>
<TR >
| <TD >
| <FONT SIZE="6">
| A Purely Functional Language
</FONT>
</TD>
</TR>
</TABLE>

The nesting here is explicit. This is especially useful when you have a non-trivial piece of code that generates Html, and you want to see whats actually happening.

You can use the mechanism used by the debugHtml function to display your own nesting structures! Just translate your data into an HtmlTree structure.

`data HtmlTree` `` `=` `HtmlLeaf` `[``Html``]` `` `|` `HtmlNode` `[``Html``] [``HtmlTree``] [``Html``]`

HtmlLeaf are printed without background color, and HtmlNode displays the first [Html] as a header, the subtrees nested, and the second [Html] as a footer. Here is how we transliterate the Html structure in the debugging code

`debug` `::` `Html` `->` `HtmlTree` `debug` `(``HtmlString str``) =` `HtmlLeaf` `(``spaceHtml` `:` `markup str``)` `debug` `(``HtmlTag` `{` `` `thetag` `=` `thetag``,` `` `innerHtml` `=` `innerHtml``,` `` `attrs` `=` `attrs` `` `}) =` `` `case innerHtml of` `` `[] ->` `HtmlNode` `[``hd``] [] []` `` `xs` `->` `HtmlNode` `[``hd``] (``map debug xs``) [``tl``]` `` `where` `` `args` `=` `unwords` `(``map show attrs``)` `` `hd` `=` `font` `!` `[``size` `"1"``]` `<<` `(``"<"` `++` `thetag` `++` `" "` `++` `args` `++` `">"``)` `` `tl` `=` `font` `!` `[``size` `"1"``]` `<<` `(``"</"` `++` `thetag` `++` `">"``)`

HtmlTree is an instance of MARKUP, so if you have a tree called treeExample, you can use:

`exampleTree` `::` `HtmlTree` `example` `:: [``Html``]` `example` `=` `br` `+++` `exampleTree` `+++` `br`