[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: how expressive are they?



Noel Welsh writes:
> (xml-macro book
>      (xml-rules
>       ((_ title: bt
>           (poem title: t poet: a tag: m
>                 (stanza (line l1) (line l) ...) ...) ...)
>        (xml-template
>           (h4:html
>            (h4:head (h4:title bt))
>            (h4:body
>              (h4:h1 bt)
>              (h4:p) "Table of Contents:"
>              (h4:ul (h4:li (h4:a h4:href: (string-append "#" m) t)) ...)
>              (h4:div (h4:p) (h4:a h4:name: m) (h4:strong t) (h4:br) (h4:em a)
>                      (list (h4:p) l1 (list (h4:br) l) ...) ...) ...))))))
> 
> Rewrite that as succinctly and clearly in
> Perl/Smalltalk/your-favourite-PL

First, I want to point out that I had to read the R5RS section on
macros to understand the ... bits, and then I still didn't understand
them --- I had to read some of the examples.  I finally understood ...
when I read the definition of "let" as a macro.  It still took me a
fair bit of C-M-n and C-M-p to see what the scope of the ...'s in the
template were, so I think the clarity leaves a bit to be desired.  I
certainly can't complain about the succinctness, though.

You could rewrite the pattern without macros as follows:

(lambda (book)
  (h4:html
   (h4:head (h4:title (book 'bt)))
   (h4:body
    (h4:h1 (book 'bt))
    (h4:p) "Table of Contents:"
    (h4:ul (lambda (poem) 
	       (h4:li (h4:a 
		         h4:href: (string-append "#" (poem 'm)) (poem 't)))))
    (lambda (poem)
	(h4:div (h4:p)
		(h4:a h4:name: (poem 'm)) 
		(h4:strong (poem 't)) (h4:br) (h4:em (poem 'a))
		(lambda (stanza)
		    (list (h4:p) (stanza 'l1) 
			  (lambda (line) (list (h4:br) (line 'l))))))))))

I find this clearer, though admittedly it contains more characters,
and I wrote it, so I might suffer from some bias here.  The ...'s have
become lambdas, because their bodies get evaluated zero or more times,
as determined by the number of times something[0] in the pattern
matched.

So we can rewrite the whole thing more clearly as follows, in Scheme
without macros:

(define book 
  (xml-macro
   (xml-rules
    `((_ title: bt
	 (poem title: t poet: a tag: m
	       (stanza (line l1) (line l) ...) ...) ...)
     ,(lambda (book)
       (h4:html
	(h4:head (h4:title (book 'bt)))
	(h4:body
	 (h4:h1 (book 'bt))
	 (h4:p) "Table of Contents:"
	 (h4:ul (lambda (poem) 
		  (h4:li (h4:a 
			  h4:href: (string-append "#" (poem 'm)) 
			  (poem 't)))))
	 (lambda (poem)
	   (h4:div 
	    (h4:p)
	    (h4:a h4:name: (poem 'm)) 
	    (h4:strong (poem 't)) (h4:br) (h4:em (poem 'a))
	    (lambda (stanza)
	      (list (h4:p) (stanza 'l1) 
		    (lambda (line) (list (h4:br) (line 'l)))))))))))))

I'd still rather write it like this:

class book(xmlmacro):
    def book(self, book):
        return h4.html(
            h4.head(h4.title(book.title)),
            h4.body(
                h4.h1(book.title),
                h4.p(), "Table of Contents:",
                h4.ul(
                    [h4.li(h4.a(poem.title, href=("#" + poem.tag)))
                     for poem in book.poem]
                ),
                [h4.div(h4.p(), h4.a(name=poem.tag), h4.strong(poem.title),
                        h4.br(), h4.em(poem.poet),
                        [(h4.p(), stanza.line[0],
                         [(h4.br(), line.content) for line in stanza.line[1:]])
                        for stanza in poem.stanza])
                for poem in book.poem]
            )
        )

I can read that much more easily, even though it contains 30% more
characters.

I wrote most of the output side of that a couple of years ago, except
for the flattening bit, and I think I could write the input side
quickly.  I guess I'll post it to kragen-hacks when I get a chance.
It works in ordinary Python, no macros.

I'd like it better if I could write the list-comprehensions another way:
    for poem in book.poem:
        h4.li(h4.a(poem.title, href=("#" + poem.tag)))
but you gotta do what you gotta do.

[0] "Something"?  That sounds like a problem.  It happens to work for
this simple example, because all the ...'s in the pattern nest neatly
inside one another, but it runs into trouble when there are a couple
of different things that might repeat different numbers of times in
the input.  The normal macro-expander can look inside the ...ed
template block to see which pattern variables it uses, then ensure
that they match the same number of times in the input, but some poor
template engine that only gets lambdas can't do that.

In the Python version, I specified what each iteration construct
iterated over, so I didn't have that problem.

-- 
<kragen@pobox.com>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
Edsger Wybe Dijkstra died in August of 2002.  The world has lost a great
man.  See http://advogato.org/person/raph/diary.html?start=252 and
http://www.kode-fu.com/geek/2002_08_04_archive.shtml for details.