Heist Gets Blaze Syntax
Update: Heist has switched from using hexpat to using xmlhtml as of version 0.5. You can still use blaze syntax as described in this post, but you have to import Text.Blaze.Renderer.XmlHtml instead.
Users of Heist, my Haskell templating system, got a very nice surprise for Christmas this year when Jasper Van der Jeugt uploaded a new package blaze-html-hexpat to Hackage. If you're not familiar with Heist, you should probably check out the tutorial before reading further. Heist uses the Haskell XML library hexpat for XML template parsing and internal representation. Users are encouraged to put as much markup as possible into templates and keep Haskell-generated markup to a minimum. However, it is impractical to do this 100% of the time.
When the situation arises where you do want to generate bits of markup in code, you have to generate hexpat data structures. Hexpat does provide some convenience functions for this purpose (see the Tree and NodeClass modules), but most would agree that it's a rather poor way to generate HTML from Haskell. Here's an example:
With the new blaze-html-hexpat package, we can use the same DSL used by blaze-html. This allows us to rewrite the above code like this:
Ahhhhh, much nicer. Blaze is encoding-aware, so it works better with Text than ByteString. However, we could have done a manual conversion or used H.unsafeByteString instead of H.text if we had wanted to stick with the ByteStrings. The renderHtml function comes in the new blaze-html-hexpat package and generates the Hexpat Node list required by splices. Here are the imports blaze-related imports needed for this code:
Blaze is only concerned with generation of HTML, so you will still need to use the Hexpat API for HTML processing. But I think this will be a big help for those places where you need Haskell generated HTML for Heist templates.
Users of Heist, my Haskell templating system, got a very nice surprise for Christmas this year when Jasper Van der Jeugt uploaded a new package blaze-html-hexpat to Hackage. If you're not familiar with Heist, you should probably check out the tutorial before reading further. Heist uses the Haskell XML library hexpat for XML template parsing and internal representation. Users are encouraged to put as much markup as possible into templates and keep Haskell-generated markup to a minimum. However, it is impractical to do this 100% of the time.
When the situation arises where you do want to generate bits of markup in code, you have to generate hexpat data structures. Hexpat does provide some convenience functions for this purpose (see the Tree and NodeClass modules), but most would agree that it's a rather poor way to generate HTML from Haskell. Here's an example:
postSplice :: ByteString -> ByteString -> ByteString -> Splice Application postSplice title author body = do return $ [X.Element "div" [("class","post")] $ [ X.Element "h1" [("class", "post_title")] $ [X.Text title] , X.Element "div" [("class", "post_author")] $ [X.Text author] , X.Element "div" [("class", "post_body")] $ [X.Text body] ] ]
With the new blaze-html-hexpat package, we can use the same DSL used by blaze-html. This allows us to rewrite the above code like this:
postSplice2 :: Text -> Text -> Text -> Splice Application postSplice2 title author body = return . renderHtml $ do H.div ! A.class_ "post" $ do H.h1 ! A.class_ "post_title" $ H.text title H.div ! A.class_ "post_author" $ H.text author H.div ! A.class_ "post_body" $ H.text body
Ahhhhh, much nicer. Blaze is encoding-aware, so it works better with Text than ByteString. However, we could have done a manual conversion or used H.unsafeByteString instead of H.text if we had wanted to stick with the ByteStrings. The renderHtml function comes in the new blaze-html-hexpat package and generates the Hexpat Node list required by splices. Here are the imports blaze-related imports needed for this code:
import Text.Blaze.Html5 (Html, (!)) import qualified Text.Blaze.Html5 as H import qualified Text.Blaze.Html5.Attributes as A import Text.Blaze.Renderer.Hexpat
Blaze is only concerned with generation of HTML, so you will still need to use the Hexpat API for HTML processing. But I think this will be a big help for those places where you need Haskell generated HTML for Heist templates.
Comments
In case you're interested, here are my plans for hexpat: 1. Keep API stable, 2. improve speed, 3. Finish hexpat-iteratee.
I'd appreciate any ideas for improvement.
Real life was not so kind though:
testSplice :: Splice Application
testSplice = return . renderHtml $ do
H.text $ pack "Bingo!"
Couldn't match expected type `blaze-html-0.3.2.1:Text.Blaze.Internal.Html'
against inferred type `Html'
In the expression: H.text $ pack "Bingo!"
In the second argument of `($)', namely
`do { H.text $ pack "Bingo!" }'
In the expression:
return . renderHtml $ do { H.text $ pack "Bingo!" }
That error looks as if blaze-html-expat was built against a different version of blaze-html than what you're importing H.text from.
What happens if you try to build your example in a Cabal project?
thanks for this package ! I'm wondering if I could find somewhere a good analysis of various (X)HTML generation solutions in Haskell, and the rationals behind them. Very naively, I'd think that the closer it stick to plain Haskell (like Blaze), the safer and the more composable. But I'm sure I'm missing some very attracting use cases of templating languages such as Heist, so I'd really like to be enlightened on them.