`htm` in its macro-scope, which causes further occurences of `htm` to
be expanded, which allows nested HTML-in-Lisp-in-HTML-... expressions.
-To put this convoluted[^3] explanation in simpler words:
+To put this convoluted[^3] explanation in simpler[^4] words:
`with-html-output` throws us into a HTML template context; `loop` (or
some other CL control structure) gets us out of there, but `htm`
brings us back in, which is precisely the "mix HTML and CL" machinery
down, which exercise unfortunately goes way beyond the scope of
this humble article.
+[^4]: **Update**, July 6: The esteemed readers inform me that I'm all
+ over [the place][btcbase-1921771] with my attempt at helping them
+ make sense of this, and I doubt that my [second
+ attempt][btcbase-1921902] is helping much either. So let's take a
+ step back and do a third attempt.
+
+ Take any arbitrary (Common) Lisp function `f`, that we've just
+ finished writing. We naturally want to execute this new function.
+ In order to do that, the function gets compiled and then the
+ resulting code gets evaluated. In fact there's much more happening
+ there, but I'm trying to get the general readership familiar with
+ this, so bear with me, mkay?
+
+ So in our scenario, before the compilation phase, one of the steps
+ involves taking the expressions used within `f` that were
+ previously defined as "CL macros" and macroexpanding them. That
+ is, there is, potentially, code in `f` that gets executed *at
+ compile-time*. Most of the time we're doing this because a. we
+ want to preserve code modularity; while b. not fucking up run-time
+ performance; but there's a deeper consideration to be had in mind,
+ namely that CL macros allow the user to define "sub-languages"
+ that, when employed correctly, will result in code that fits in
+ head.
+
+ Let us, for example, say that we wanted to output a HTML page that
+ contained "Hello, \$name!", where "\$name" is to be replaced with
+ a user-provided variable. With nothing but our bare-bones Lisp, we
+ would write something along the lines of:
+
+ ~~~~ {.commonlisp}
+(defun f (name stream)
+ (write-string "<html><body>Hello, " stream)
+ (write-string name stream)
+ (write-string "!</body></html>"))
+~~~~
+
+ and then we'd say, e.g.:
+
+ ~~~~
+> (f "spyked" *standard-output*)
+<html><body>Hello, spyked!</body></html>
+~~~~
+
+ which does precisely what we want, only this quickly gets uglier
+ with the amount of content we put in our HTML page. So one thing
+ we could do is to wrap some of our previous code into macros:
+
+ ~~~~ {.commonlisp}
+(defmacro html-body (stream)
+ `(write-string "<html><body>" ,stream))
+;;
+(defmacro hello-to (name stream)
+ `(progn
+ (write-string "Hello, " ,stream)
+ (write-string ,name ,stream)
+ (write-string "!" ,stream)))
+;;
+(defmacro /body-/html (stream)
+ `(write-string "</body></html>" ,stream))
+~~~~
+
+ Notice how the macro definitions wrap code that we're *not*
+ executing at compile time into backquotes and commas, which form a
+ syntactic mechanism for controlling evaluation. This footnote is
+ becoming a blogpost of its own, so I won't get into further
+ details on the particular topic.
+
+ Getting back to our `f`, its definition becomes:
+
+ ~~~~ {.commonlisp}
+(defun f (name stream)
+ (html-body stream)
+ (hello-to name stream)
+ (/body-/html stream))
+~~~~
+
+ which looks a lot better than the previous, but does the same
+ thing. Say we wanted to look at the macro expansion process by
+ ourselves. Then we would do:
+
+ ~~~~ {.commonlisp}
+> (macroexpand-1 '(hello-to "spyked" *standard-output*))
+(PROGN
+ (WRITE-STRING "Hello, " *STANDARD-OUTPUT*)
+ (WRITE-STRING "spyked" *STANDARD-OUTPUT*)
+ (WRITE-STRING "!" *STANDARD-OUTPUT*))
+~~~~
+
+ What CL-WHO does is to generalize this HTML-outputting mechanism
+ into its own templating language. For the sake of illustrating how
+ growing complexity is handled, we can rewrite a variation on the
+ example above in CL-WHO as:
+
+ ~~~~ {.commonlisp}
+ (defun f (name stream)
+ (cl-who:with-html-output (stream)
+ (:html (:body
+ (cl-who:str "Hello, ")
+ (if (string= name "spyked")
+ (cl-who:htm (:b (cl-who:str "my man")))
+ (cl-who:str name))
+ (cl-who:str "!")))))
+~~~~
+
+ which works like:
+
+ ~~~~ {.commonlisp}
+> (f "gigi" *standard-output*)
+<html><body>Hello, gigi!</body></html>
+> (f "spyked" *standard-output*)
+<html><body>Hello, <b>my man</b>!</body></html>
+~~~~
+
+ and expands to:
+
+ ~~~~ {.commonlisp}
+ > (macroexpand-1
+ '(cl-who:with-html-output (stream)
+ (:html (:body
+ (cl-who:str "Hello, ")
+ (if (string= name "spyked")
+ (cl-who:htm (:b (cl-who:str "my man")))
+ (cl-who:str name))
+ (cl-who:str "!")))))
+;; The result:
+(LET ((STREAM STREAM))
+ (CHECK-TYPE STREAM STREAM)
+ (MACROLET ((CL-WHO:HTM (&BODY CL-WHO::BODY)
+ `(CL-WHO:WITH-HTML-OUTPUT (,'STREAM NIL :PROLOGUE NIL :INDENT
+ ,NIL)
+ ,@CL-WHO::BODY))
+ (CL-WHO:FMT (&REST CL-WHO::ARGS)
+ `(FORMAT ,'STREAM ,@CL-WHO::ARGS))
+ (CL-WHO:ESC (CL-WHO::THING)
+ (CL-WHO::WITH-UNIQUE-NAMES (CL-WHO::RESULT)
+ `(LET ((,CL-WHO::RESULT ,CL-WHO::THING))
+ (WHEN ,CL-WHO::RESULT
+ (WRITE-STRING (CL-WHO:ESCAPE-STRING ,CL-WHO::RESULT)
+ ,'STREAM)))))
+ (CL-WHO:STR (CL-WHO::THING)
+ (CL-WHO::WITH-UNIQUE-NAMES (CL-WHO::RESULT)
+ `(LET ((,CL-WHO::RESULT ,CL-WHO::THING))
+ (WHEN ,CL-WHO::RESULT (PRINC ,CL-WHO::RESULT ,'STREAM))))))
+ (WRITE-STRING "<html><body>" STREAM)
+ (LET ((CL-WHO::*INDENT* NIL))
+ NIL
+ (CL-WHO:STR "Hello, "))
+ (LET ((CL-WHO::*INDENT* NIL))
+ NIL
+ (IF (STRING= NAME "spyked")
+ (CL-WHO:HTM (:B (CL-WHO:STR "my man")))
+ (CL-WHO:STR NAME)))
+ (LET ((CL-WHO::*INDENT* NIL))
+ NIL
+ (CL-WHO:STR "!"))
+ (WRITE-STRING "</body></html>" STREAM)))
+~~~~
+
+ Maybe not the most beautiful piece of code, but it all becomes
+ clear once you become familiar with the CL-WHO implementation.
+
+ So, to summarize: CL-WHO is a compiler that parses a templating
+ language that represents HTML nodes as Lisp lists starting with
+ keywords, e.g. ":em" is the representation of "<em>"; but
+ the same language allows us to express mechanical tasks,
+ e.g. looping over a list of items, which requires a special symbol
+ (`cl-who:htm`) to move us back in the "HTML page" context. The
+ *result* of a "with-html-output" code is a program that outputs
+ HTML to wherever we want.
+
+ So then, I guess that makes CL-WHO a HTML generator generator?
+
[btcbase-1919627]: http://btcbase.org/log/2019-06-23#1919627
[hunchentoot-i]: /posts/y05/093-hunchentoot-i.html
[btcbase-1919634]: http://btcbase.org/log/2019-06-23#1919634
[cl-who-syntax]: http://archive.is/3kH5V#selection-835.0-835.20
[cl-who-demo]: /uploads/2019/07/cl-who-demo/
[coad]: http://coad.thetarpit.org/
+[btcbase-1921771]: http://btcbase.org/log/2019-07-05#1921771
+[btcbase-1921902]: http://btcbase.org/log/2019-07-06#1921902