<div>I heartily approve of the introduction of MAYBE-ONCE-ONLY in a5cf0df,</div><div>because it drives me nuts (perhaps irrationally) to see superfluous</div><div>bindings in generated code. If nothing else, they detract from PS's</div>

<div>goal of producing readable JS.</div><div><br></div><div>Upgrading our codebase to use the latest Parenscript from git, I</div><div>noticed this macro and saw an opportunity to remove a bit of code </div><div>we wrote that does the same thing. Doing so, I noticed the following</div>

<div>four things.</div><div><br></div><div>1. MAYBE-ONCE-ONLY wasn't being exported, so I modified package.lisp</div><div>to do that. Should "ps" be in the name, in that case? MAYBE-ONCE-ONLY</div><div>is a good name (it certainly kicks the name we were using's butt, as</div>

<div>you will see below), but already a little on the verbose side, and</div><div>MAYBE-PS-ONCE-ONLY is over my comfort limit in that respect. So I left</div><div>the name as is.</div><div><br></div><div>2. There is a problem with symbol macros leading to multiple</div>

<div>evaluation:</div><div><br></div><div>  (defpsmacro true? (x)</div><div>    (maybe-once-only (x)</div><div>      `(and (!null ,x) (not (= ,x false)))))</div><div><br></div><div>  (define-ps-symbol-macro aaa (bar))</div>

<div><br></div><div>  (defun foo ()</div><div>    (symbol-macrolet ((bbb (bar)))</div><div>      (when (true? bbb)</div><div>        (baz)))</div><div>    (when (true? aa)</div><div>      (baz)))</div><div><br></div><div>

=> function foo() {</div><div>    if (bar() != null && bar() !== false) {</div><div>        baz();</div><div>    };</div><div>    return bar() != null && bar() !== false ? baz() : null;</div><div>};</div>

<div><br></div><div>Note that this is true for both kinds of symbol macro — top-level and</div><div>local — as the example shows.</div><div><br></div><div>3. The generated code evaluates bound expressions in reverse order of</div>

<div>appearance in the code. I don't know if this would ever be a problem,</div><div>but the principle of least surprise probably cautions against it. That</div><div>is, in the following form, (bar) should probably be evaluated before (baz):</div>

<div><br></div><div>  (defmacro+ps blah (a b)</div><div>    (maybe-once-only (a b)</div><div>      `(list ,a ,b)))</div><div><br></div><div>  (defun foo ()</div><div>    (blah (bar) (baz)))</div><div><br></div><div>=> function foo() {</div>

<div>    var b3600 = baz();</div><div>    var a3599 = bar();</div><div>    return [a3599, b3600];</div><div>};</div><div><br></div><div>4. Finally, here for comparison purposes is the macro I removed from</div><div>our code in order to use MAYBE-ONCE-ONLY instead. It has a worse name,</div>

<div>the horrible ONCE-WHEN (I couldn't come up with anything better), but</div><div>I think I prefer its implementation for a couple of reasons: it</div><div>doesn't suffer from the two problems (symbol macros and evaluation</div>

<div>order) mentioned above, and it delegates to the classic ONCE-ONLY</div><div>(PS-ONCE-ONLY in our case) for all of the macro magic, which makes it</div><div>shorter and easier to follow.</div><div><br></div><div>  (defun atomic? (expr)</div>

<div>    (or (atom expr) (atom (ps::ps-macroexpand expr))))</div><div><br></div><div>  (defmacro once-when (vars &body body)</div><div>    (cond ((= (length vars) 1)</div><div>           `(cond ((atomic? ,(car vars)) ,@body)</div>

<div>                  (t (ps-once-only (,(car vars)) ,@body))))</div><div>          (t `(once-when (,(car vars))</div><div>                (once-when ,(cdr vars)</div><div>                  ,@body)))))</div><div><br></div>

<div>Perhaps the two implementations can be combined into something better.</div><div><br></div><div>Daniel</div><div><br></div>