views:

49

answers:

2

Hi

I have the following code to generate a blank html page with a series of divs with id's and classes in Haskell using the Text.XHtml.Strict library:

module Main where

import Text.XHtml.Strict
import Text.Printf


page :: Html
page = pHeader +++ pTop +++ pBody +++ pFooter

pHeader :: Html
pHeader = header << thetitle << "Page title" 

pTop :: Html
pTop = (dC "header") << (dI "title") 

pFooter :: Html
pFooter = (dC "footer") << (dI "foottext")

pBody :: Html
pBody = body << (dC "main") << (dI "window") << (dI "content")

dC :: String -> Html
dC x = (thediv noHtml)! [theclass x]

dI :: String -> Html
dI x = (thediv noHtml) ! [identifier x]

main :: IO ()
main = do
    printf $ prettyHtml $ page

The functions dC and dI should make an empty with a class or id respectively. In the interpreter these functions work fine when concatenating, as in:

 printf $ prettyHtmlFragment $ dC "1" +++ dC "2"
<div class="1">
</div>
<div class="2">
</div>

But not when I try to nest them using << instead of +++, I get an error:

<interactive>:1:28:
    Couldn't match expected type `Html -> b'
           against inferred type `Html'

This is what I think the cause of the problem in the main part of the code is, but I don't know how to fix it. Any ideas?

+1  A: 

dC and cI can't nest because they're defined to be empty. You need to leave a hole that the nested piece will go into. The hole is expressed by parameterizing it:

dC classx child = (thediv child)! [theclass classx] 
dI x y = (thediv y) ! [identifier x] 

likewise for body:

pBody x = body << (dC "main") << (dI "window") << (dI "content" x) 
ja
+2  A: 

Just take out the nohtml parts, and change the signatures:

dC :: String -> Html -> Html
dC x = thediv ! [theclass x]

dI :: String -> Html -> Html
dI x = thediv ! [identifier x]

The ! operator can add attributes not only to an Html object, but also to a function that returns Html. thediv is a function that takes Html and returns it wrapped in a <div> element. By applying ! to it, you create a function that takes Html and wraps a <div class="…"> (or id="…") around it.

> :type thediv
thediv :: Html -> Html

> let dC c = thediv ! [theclass c]
> let dI i = thediv ! [identifier i]

> :type dC
dC :: String -> Html -> Html
> :type dI
dI :: String -> Html -> Html

> putStr $ prettyHtmlFragment $ body << dC "main" << dI "window" << dI "content" << "Hi"
<body>
   <div class="main">
      <div id="window">
         <div id="content">
            Hi
         </div>
      </div>
   </div>
</body>

Note that you don't need the extra parenthesis.

MtnViewMark
Thanks for this, the documentation for this library is hard to come by (except for the hackage documentation) and tutorials are practically nonexistent.
Jonno_FTW
When looking for doc or examples, remember that the Text.Xhtml package was based on the Text.Html package, and has almost exactly the same usage.
MtnViewMark