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
  +++ << 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  :: -> [Html] }
infixr <<
(<<) :: (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 +++  -- combining Html
(+++) :: (MARKUP a,MARKUP b) => -> -> [Html]
+++ 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
      (!) :: -> [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