I have used a similar approach for generating html for a while and like a lot.
The idea came from Eli Barzilay's scribble/html
.
The idea is to define a constructor function for each html element.
The function a
constructs links, p
constructs paragraphs etc.
Having a constructor for each element has a couple of advantages.
First, construction of html elements can be done without juggling quotes, quasiquotes and unquotes.
As an example
(p "This is" (b "bold") ".")
will construct a paragraph.
Second, it's impossible to misspell html elements. An attempt to write
(p "This is" (bold "bold") ".")
will result in an error:
bold: undefined;
cannot reference an identifier before its definition
Catching the error here as opposed to later in the browser is great.
Third, an constructor can validate the arguments. For example the line break element br
can't contain text, so (br "foo")
could generate an error.
An example
(define (color-table names rgb-strings)
(table
(for/list ([name names] [rgb rgb-strings])
(tr (td name) (td rgb)))))
(~x (color-table '("black" "red" "green") '("000000" "ff0000" "00ff00")))
The output is (linebreaks inserted by be to make it readable) on this forum.
"<table><tr><td>black</td><td>000000</td></tr>
<tr><td>red</td><td>ff0000</td></tr>
<tr><td>green</td><td>00ff00</td></tr></table>"
The above list of advantages apply when each html element has its own constructor, which includes your situation. The representation used internally by scribble/html
doesn't represent each element by its own structure kind though. Instead it has a structure element
that is used to represent all element types. For programs that read html pages and analyze them, the actual representation matter. For web apps that only need to produce html, the actual representation is not a matter of concern (as long as it fast to produce a string representation from it).
A great idea also stolen from scribble/html
is autoquoting of symbols ending in colon.
This is convenient when element constructors allow attributes:
(span style: "color: red;"
"This text is red.")
This constructor style works great with at-expressions.
The example:
(p "This is" (b "bold") ".")
becomes
@p{This is @b{bold}.}
The previous example becomes:
@span[style: "color: red;"]{This text is red.}
Due to a technicality instead of using the structs from scribble/html
directly, I use urlang/html
.
(I should probably have put it elsewhere since it is unrelated to the rest of Urlang).
A nice little utility from urlang/html
is html->at-exp
which does what it says on the tin.
> (require urlang/html)
> (html->at-exp "<p><b>foo</b></p>")
"@p{@b{foo}}"
Similarly, output from Neil Van Dyke's html-parsing
library can also be converted to at-expressions.
> (require html-parsing)
> (xexp->at-exp (html->xexp "<p><b>foo</b></p>"))
"@p{@b{foo}}"
Finally ~x
and ~xs
are provided. They convert a single element and a list of elements respectively to a string.
With regards to performance: I have never measured. Construction of lists are fast, but so is structure construction.