Discussion:
Macroexpansion of with-html-output body? (follow-up)
Vsevolod
2008-07-25 20:28:00 UTC
Permalink
Hi,

Once again (hopefully, for the last time) I want to address the
question "to use or not to use", so to say, macroexpansion inside
w-h-o form, and moreover, how to do it right.

I have experimented with the previously proposed solution of
introducing additional EMB keyword and have found, that it as well has
some drawbacks. More precisely, they exist because of the nature of
the w-h-o macro itself, which uses special processing for the forms,
contained inside HTM: they are processed before any function
application, that is why if you EMBed some macros, which use HTM
keyword, which should output to their own streams, the result can be
emitted in the unexpected order. To illustrate this vague statement, I
can show the piece of the code, that combines cl-who and parenscript
forms to make a currency selection form with some javascript backing:

(defmacro cur-sel ()
(w/uniqs (cur)
(let ((sel-str `(:select :id "currency"
(dolist (,cur *currencies*)
(htm (:option :selected (string= ,cur *default-currency*)
(str ,cur)))))))
`(htm
(emb (ps-script* (write-htm (:a :class "toggle" :onclick (ps-inline*
`(lambda ()
,(write-htm ,sel-str)))
(str (ie-val *default-currency*))))))
(:noscript ,sel-str)))))

(defmacro write-htm (form)
``(.write document
,(who
,form)))

The macro CUR-SEL, that is intended to itself be EMBedded, uses
WRITE-HTM to emit HTML forms to string, that will in turn be an
argument to javascript document.write(). But, as after macroexpansion
of sel-str HTM forms appear as arguments to WRITE-HTM, the output of
the containing w-h-o forms goes to the stream, bound by the topmost
w-h-o and not the innermost, as it's supposed...

Anyway, after some re-thinking of a problem, I've come to a
conclusion, that my approach was not the most natural one. Now, I
believe, that the more lispy way is to view w-h-o macro as solely a
good processor for static pseudo-html data (like '(:p some text (:br)
(:a :href ....), and on top of it build macros, which will allow
eventually to add macroexpansion ability.

My initial target was to be able to use macros with pseudo-html forms,
like in the following simple example:
(def-who-macro (arg)
`(:p some text (:br) (:a :href ,arg)))
inside w-h-o body the same way you would use an ordinary macro, but
I'd got carried away with modifying the w-h-o macro itself to allow
for such usage. It turned out, that it's not hard to implement the
separated approach. My variant is below:

(defmacro def-who-macro (name (&rest args) pseudo-html-form)
"Produces functions for generating pseudo-html forms,
which can be embedded in WHO-PAGEs"
`(defun ,name (,@args)
,pseudo-html-form))

(defmacro def-who-page (name (&optional stream) pseudo-html-form)
"Creates a function to generate an HTML page with the use of
WITH-HTML-OUTPUT macro, in which pseudo-html forms, constructed with
DEF-WHO-MACRO, can be embedded. If STREAM is nil/not supplied
WITH-HTML-OUTPUT-TO-STRING to a throwaway string will be used,
otherwise -- WITH-HTML-OUTPUT"
`(macrolet ((who-tmp ()
`(,@',(if stream `(with-html-output (,stream nil :prologue t))
`(with-html-output-to-string (,(gensym) nil :prologue t)))
,,pseudo-html-form)))
(defun ,name ()
(who-tmp))))

(defmacro who (pseudo-html-form)
"An analog of DEF-WHO-PAGE, which not only creates an
w-h-o pseudo-HTML to HTML translation function, but as well
runs it and emits the obtained HTML as a string"
`(macrolet ((who-tmp ()
`(with-html-output-to-string (,(gensym))
,,pseudo-html-form)))
(who-tmp)))


And the example usage can be:

(def-who-macro ps-script (&body body)
`(:script :type "text/javascript"
(fmt "~%// <![CDATA[~%")
(str (js:ps ,@body))
(fmt "~%// ]]>~%")))

(def-who-page demo-page ()
`(:html (:head ,(:ps-script
(defun hi ()
(alert "Hello world!"))))
(:body (:input :type "button" :value "Click here..."))))

The only catch is that DEF-WHO-MACRO actually generates not a macro in
Lisp terms, but a function, which constructs lists of pseudo-HTML
forms.

I hope, the given code will be useful.
Best regards,
Vsevolod Dyomkin
Leslie P. Polzer
2008-07-29 07:36:48 UTC
Permalink
Have you looked at Parenscript's macro implementation?
It seems more natural to use.

It would be great to have this in mainline when it's clean.
Vsevolod
2008-07-29 09:09:01 UTC
Permalink
If you mean process-html-forms (from lib/js-html as of 20071104 release), as afar as I understand, it's just a simplistic alternative to CL-WHO.

While all the code examples in the manual are for Allegro's net.html.generator library (more info can be seen here: http://common-lisp.net/pipermail/parenscript-devel/2008-April/000180.html)...

And, speaking about naturalness of use, that was exactly my starting point, when I tried to use macros with (as I call them for a lack of a better name) pseudo-html-forms inside w-h-o body just like it could be done for macros with ordinary forms inside of other lisp forms, and saw that it's not possible, because pseudo-html-forms inside w-h-o are not evaluated by the same rules as lisp forms. So I thought, that maybe a good idea would be to add another layer of special processing with the EMB keyword (http://common-lisp.net/pipermail/cl-who-devel/2008-June/000156.html), the same as STR, FMT etc. But keywords add complexity, because you should remember their usage rules. Once again it's not lispy enough, so to say. That's why I moved to the next idea.

To conclude, I don't see a way to integrate fully implicit macroexpansion in with-html-output without a substantial refactoring of the library code. By the way, you can see one such attempt here: http://common-lisp.net/pipermail/cl-who-devel/2008-February/000127.html

But, again, there arises a dilemma: should there be syntactic hints in the code, that a 'special' cl-who-macro is used. In this variant, there are none. In the variants, I've proposed, there is either EMB keyword or a comma. That's an open topic for discussion, which, I think, can be resolved by Edi Weitz, who will determine how to implement that himself.

P.S. Btw, I think, the above link can as well be of interest in the weblocks group.
Post by Leslie P. Polzer
Have you looked at Parenscript's macro implementation?
It seems more natural to use.
It would be great to have this in mainline when it's clean.
_______________________________________________
cl-who-devel site list
http://common-lisp.net/mailman/listinfo/cl-who-devel
Loading...