
Tobias Dammers wrote:
You can do similar things with Haskell EDSL-style template systems such as Blaze...
Still, the big picture with these is that template building blocks are language elements, and template code and regular code can be mixed freely. The `h1` function from Blaze is just another Haskell function; it takes a MarkdownM () value and returns a MarkdownM (), and because MarkdownM happens to be a Monad, you can use do notation to sequence HTML elements. MarkdownM () is also an instance of IsString, so if you enable -XOverloadedStrings, you can use string literals to produce HTML text nodes. This means that you can write, in plain Haskell:
h1 $ do span "Hello, " span "world!"
...and it'll produce <h1><span>Hello, </span><span>world!</span></h1>.
In this regard it is worth mentioning HSXML http://okmij.org/ftp/Scheme/xml.html#typed-SXML The document in HSXML is a monoid rather than a monad. It is hard to find a good reason for the document be a monad except for using a do notation, which is not needed in HSXML. The above "Hello, World!" example looks in HSXML as h1 (span "Hello") (span "world!") (Note that we don't need the space after `Hello'.) This produces the desired output (in plain text, HTML or XML). If we don't need span, we can just write h1 "Hello" "world!" The main difference from Blaze shows up if we attempt h1 (p "Hello") "world!" It will be a type error. The error message says No instance for (Build (DC CT_inline d0) (DC CT_block d0) t) arising from a use of `h1' No instance for (Build (DC CT_inline d0) (DC CT_block d0) (DC CT_inline d0)) arising from a use of `p' That is, (p "Hello") produces block-level content and h1 requires its children to be inline-level. HSXML thus implements the block/inline content model of HTML. Arbitrary many content content-level tags may be defined (in fact, HSXML, besides block/inline elements uses document-, attribute-, and table-level elements). It is possible for the same element to be usable in different contexts (e.g., 'title' can be either an attribute or an element used within 'head'). It is rendered differently in different contexts. Although the document itself is just a monoid rather than a monad, rendering a document can be in a monad. For instance, one of the rendering engines works in an IO monad so it can validate local links as the document is rendered. The resulting document does not have dead local links.
For the common case where you have a "master" template and an incarnation, you could write a master template that takes its inner blocks as arguments, e.g.:
It is very easy to do that in HSXML. Here is an example, the top template for the ChangeLog: toHTML :: MDoc d => CLHead d -> DC CT_root d toHTML (CLHead attrs updates) = document (head (title (cdata (ha_title attrs))) (meta_tag (description (cdata (ha_description attrs)))) author_address (meta_tag (pub_date (ha_DateRevision attrs))) (head_link LR_top (href (ha_top attrs)) (title "All you can find here")) ) (body (h1 "Log of changes on" (aref (ha_top attrs) "this site")) (p nbsp) updates (change_log_prev (ha_history_first attrs)) (change_log_prev (ha_history_last attrs)) ) (One could omit many parentheses using $, but I like parentheses in this context).