[Zope-dev] Conditionals in XML

Don Hopkins xardox@mindspring.com
Thu, 9 May 2002 23:27:42 -0700


Jeffrey P Shell writes:
> You would at the very least need something like:
> <condition>
>  <if>...</if>
>  <elif>...</elif>
>  <else>...</else>
> </condition>
>
> Which would ensure / cut back on needless growth of stacks and/or global
> variables.
>
> Personally, I think the way Page Templates can do it (via a 'not'
> expression) is fine.  When I used it today (and in the past), I never felt
> myself missing 'else', because there's not really an 'if' to begin with.
> Just conditions.  It keeps TAL light, and lets TALES take on the lifting
of
> how to write those conditions.


I've been banging my head against the problem of how to elegantly express
"if/else/ifelse" structures in xml, so this is an interesting discussion!
Any easy sleazy solution (like dtml's approach) that breaks XML syntax or
structure is right out, so it requires a different approach.

I like Jeffrey's idea:
The condition comes first (outside), and the if and else that use it are
nested inside:

<condition expr="...">
  <if> ... </if>
  <elif> [I'm not sure how this clause works: where does its condition go?]
</elif>
  <else> ... </else>
</condition>

It reads easily (with the condition obviously first and up top), and blocks
the code off nicely.
The elif I'm not so clear on. It might have its own expr="..." (or
whatever), which would break the symmetry and imply that "if" could also
contain a conditional expression.

I haven't used TAL yet, so I'm not really qualified to judge. But I'm not
really happy with the attribute based approach.
I'd rather use an XML based macro language with the full capabilities of
Lisp macros (just bound to the Python runtime instead of a Lisp system).
I think Python is a great implementation language for other higher level XML
based languages, like ZPT and others.

I don't like the idea of purposefully crippling a language in the misguided
quest to "separate presentation and logic".
If you have a good enough XML based language, you can use it to express both
presentation and logic, and you can cleanly and easily separate presentation
and logic because you have the power of abstraction, not because you're
forced to.

Any extra incompatible language syntax you're forced to use just add
friction and confusion and interoperability problems. Don't add another
layer unless you have to.
Don't force programmers to drop down into Python because the templating
language is too weak (nothing against Python, but I'm talking about
implementing a higher level language that is more powerful, not purposefully
weaker).
Templates should be structural transformation macros, not string
substitution.
Macros require a full powered programming language, or else they're a waste
of time because their limitations cause maintenance nightmares.
Please do not repeat the mistakes of the miserable C pre-processor macros
(or scandalous C++ templates for that matter).

Settle for nothing less than Lisp macros, and strive for something even
better.
Full power macros make it possible for programmers to implement clean,
simple application specific mini-languages, that can be neatly nested inside
of other templates, data, etc.
There are many wonderful ways of separating presentation and logic, that
Lisp macros make not only possible but also convenient and maintainable.
The standard approach of lobotomizing the presentation language forces you
to separate presentation and logic in the worst possible way (by using
another language), while sacrificing many superior approaches to solving the
problem.
Paul Graham's wonderful book "On Lisp" practically explains Lisp macros with
lots of great examples, and you can download the pdf file and read it for
free:
http://www.paulgraham.com/onlisp.html

Lisp's "cond" seems more elegant than long chains of if/elif/else.
The if/elif/else syntax requires several different keywords with a
particular ordering, so it's harder to rearrange, add and delete clauses;
therefore it's harder to maintain the code over time.
It uses different keywords for different clauses (and in xml they're
repeated twice as begin and end tags) so you have to change both of them to
add or delete additional clauses.

How can the syntax best support maintaining reliable and readable templates
and code? (C's "switch" statement and single statement if/else's without
brackets are horrible counter examples.)
Since "if/else" is more common than if/elif/else, maybe it's better to keep
"if/else" simple, use "cond" for chains, and not to support "elif" (or
"elseif" or however you spell it, which invites mistakes).

Lisp's "cond" is easier to use and more general purpose than C's
"switch/case" syntax (which I would dread to emulate in any other language,
with its horrible limitations and problems (design defects): not supporting
computed case statements, requiring "break", too many ways to make
mistakes).

Paul Graham (the author of "On Lisp") has written some other interesting
stuff about simplifying "cond", in the design of his "ARC" language.
http://www.paulgraham.com/arc.html
http://www.paulgraham.com/arcll1.html

The following is from Paul Graham's web page, telling how Common Lisp
defines cond, compared to ARC. And how ARC defines if in terms of cond. Also
notice how "it" is bound to the result of the test expression inside of
"if".

    -Don

====

cond split into cond + do (progn)


  CL:  (cond ((a x) (princ "!") (b x))
             ((c x) (d x))
             (t (e x)))

  Arc: (cond (a x) (do (pr "!") (b x))
             (c x) (d x)
                   (e x))

- Usually use if, which binds it: (if (a x) (car it))

The core of Arc is much the same as the core of McCarthy's original 1960
Lisp. The operators eval, car, cdr, cons, and quote work the same when
applied to symbols and lists (the only data types in the 1960 paper), except
that car and cdr generate errors when applied to nil.

The one operator we changed is cond. McCarthy, who wanted to keep his axioms
to a minimum, buried progn within cond. That worked for his examples, but
for programming in general you soon find you need a progn separate from the
implicit progn of cond.

Having an implicit progn in cond means every cond clause has to have an
extra pair of parentheses. McCarthy said later that he thought he had gotten
cond wrong, that it used too many parentheses, and this may be what he
meant. Arc's cond doesn't have an implicit progn, and so you don't need the
parentheses around each clause. We also omitted the t in the default clause,
which seemed to be an onion. The example in the slide shows the same code in
Common Lisp and in Arc. (Arc's do is Common Lisp progn, and pr is Common
Lisp princ.)

In Arc, cond is a low-level operator, used mainly in macroexpansions. Most
of the time programmers use if, which is exactly the same, except that
within a successful then-expression, the variable it will be bound to the
result of the test-expression.

====