From: Peter Seibel
Subject: Any backquote guru's out there.
Date: 
Message-ID: <m3d5xuio3f.fsf@javamonkey.com>
Take this backquoted expression:

  ``(,@,'(1 2 3))

All the Common Lisp's I've tried it in agree that it evaluates to (1 2 3). E.g.

  CL-USER> ``(,@,'(1 2 3))
  (1 2 3)

I'm trying to understand that by expanding it according to the rules
in Section 2.4.6.

That section says when backquotes are nested, the innermost backquote
expression is to be expanded first. So first I follow the rule that
says:

 `(x1 x2 ... xn) === `(x1 x2 ... xn . nil)

to get this:

  CL-USER> ``(,@,'(1 2 3) . nil)
  (1 2 3)

So far, so good. Now the appropriate rule to apply to the inner
backquoted expression seems to be:

  `(x1 x2 x3 ... xn . atom) may be interpreted to mean
    (append [ x1] [ x2] [ x3] ... [ xn] (quote atom))

      where the brackets are used to indicate a transformation of an
      xj as follows:

       -- [form] is interpreted as (list `form), which contains a
          backquoted form that must then be further interpreted.

       -- [,form] is interpreted as (list form).

       -- [,@form] is interpreted as form.

So I try this:

  CL-USER> `(append ,'(1 2 3) (quote nil))
  (APPEND (1 2 3) 'NIL)

And am immediately off the rails. What'd I do wrong?

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp

From: Chris Capel
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <10qqo37e680vn94@corp.supernews.com>
Peter Seibel wrote:

> Take this backquoted expression:
> 
>   ``(,@,'(1 2 3))
> 

...

> `(append ,'(1 2 3) (quote nil))

...

> And am immediately off the rails. What'd I do wrong?

I'm certainly no backquote guru, but I don't think textual substitution is
possible in this case. By forcing evaluation, though, with

'``,(append ,'(1 2 3) 'nil)

(note the extra `, and the preceeding '), SBCL gives me

'`(,@(1 2 3) ,@nil)

which when evaluated gives

(1 2 3)

. So it seems to be a matter of evaluating it and quoting it the correct
number of times. Maybe someone else can actually explain this.

Chris Capel
From: Kalle Olavi Niemitalo
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <87llcilegx.fsf@Astalo.kon.iki.fi>
Peter Seibel <·····@javamonkey.com> writes:

> So I try this:
>
>   CL-USER> `(append ,'(1 2 3) (quote nil))
>   (APPEND (1 2 3) 'NIL)
>
> And am immediately off the rails. What'd I do wrong?

I don't think you are off the rails.  The backquote syntax is
supposed to be a shorthand for a form that can be evaluated.
Because your original form began with two backquotes, the result
of evaluating the first backquote form should be another form
that can be evaluated.  In your experiments, the result was
(1 2 3).  If you pretend that can be evaluated, then so can 
(append (1 2 3) 'nil), and their results are the same under
equal, so either is a valid expansion for the backquote.
From: Peter Seibel
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <m38y8ihs8o.fsf@javamonkey.com>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Peter Seibel <·····@javamonkey.com> writes:
>
>> So I try this:
>>
>>   CL-USER> `(append ,'(1 2 3) (quote nil))
>>   (APPEND (1 2 3) 'NIL)
>>
>> And am immediately off the rails. What'd I do wrong?
>
> I don't think you are off the rails.  The backquote syntax is
> supposed to be a shorthand for a form that can be evaluated.
> Because your original form began with two backquotes, the result
> of evaluating the first backquote form should be another form
> that can be evaluated.  In your experiments, the result was
> (1 2 3).  If you pretend that can be evaluated, then so can 
> (append (1 2 3) 'nil), and their results are the same under
> equal, so either is a valid expansion for the backquote.

Ah, I see. So a better example would be ``(,@,'(list 1 2)). Then it
obviously does work, at least up to the step I showed before:

  CL-USER> ``(,@,'(list 1 2))
  (LIST 1 2)
  CL-USER> (eval *)
  (1 2)
  CL-USER> ``(,@,'(list 1 2) . nil)
  (LIST 1 2)
  CL-USER> (eval *)
  (1 2)
  CL-USER> `(append ,'(list 1 2) (quote nil))
  (APPEND (LIST 1 2) 'NIL)
  CL-USER> (eval *)
  (1 2)

Thanks.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Kaz Kylheku
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <cf333042.0412011537.6404e304@posting.google.com>
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> Kalle Olavi Niemitalo <···@iki.fi> writes:
> 
> > Peter Seibel <·····@javamonkey.com> writes:
> >
> >> So I try this:
> >>
> >>   CL-USER> `(append ,'(1 2 3) (quote nil))
> >>   (APPEND (1 2 3) 'NIL)
> >>
> >> And am immediately off the rails. What'd I do wrong?
> >
> > I don't think you are off the rails.  The backquote syntax is
> > supposed to be a shorthand for a form that can be evaluated.
> > Because your original form began with two backquotes, the result
> > of evaluating the first backquote form should be another form
> > that can be evaluated.  In your experiments, the result was
> > (1 2 3).  If you pretend that can be evaluated, then so can 
> > (append (1 2 3) 'nil), and their results are the same under
> > equal, so either is a valid expansion for the backquote.
> 
> Ah, I see. So a better example would be ``(,@,'(list 1 2)). Then it
> obviously does work, at least up to the step I showed before:

Then the derivation is like this:

[,@form] -> form  on [,@,'(list 1 2)]
    
  `(append ',(list 1 2))

[form] -> (list `form) on append and (quote ,(list 1 2))

  (append (list `append) (list `',(list 1 2)))

Reveal QUOTE to make subsequent step clear:

  (append (list `append) (list `(quote ,(list 1 2))) { reveal QUOTE }

[`form] -> 'form  over  `append backquote
`(x1 x2 ) --> (append ...)  over `(quote ...) 
where quote goes to `quote

  (append (list 'append) (list (append (list `quote) (list (list 1 2)))))

[`form] -> 'form  over  `quote

  (append (list 'append) (list (append (list 'quote) (list (list 1 2)))))

When we evaluate this, we get

  (append '(1 2))

And we can evaluate that again to get

  (1 2)
From: Reini Urban
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <41ae16ab$1@e-post.inode.at>
Peter Seibel schrieb:
> Ah, I see. So a better example would be ``(,@,'(list 1 2)). Then it
> obviously does work, at least up to the step I showed before:
> 
>   CL-USER> ``(,@,'(list 1 2))
>   (LIST 1 2)
>   CL-USER> (eval *)
>   (1 2)
>   CL-USER> ``(,@,'(list 1 2) . nil)
>   (LIST 1 2)
>   CL-USER> (eval *)
>   (1 2)
>   CL-USER> `(append ,'(list 1 2) (quote nil))
>   (APPEND (LIST 1 2) 'NIL)
>   CL-USER> (eval *)
>   (1 2)

But please don't teach this in your book, unless you want to teach how 
to obfuscate in perl-spirit.
Tricks are nice, but use them like Graham did, with proper macro names, 
explaining what they do, not just a ,@,X recipe.
-- 
Reini Urban
http://xarch.tu-graz.ac.at/home/rurban/
From: Peter Seibel
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <m3llchelvg.fsf@javamonkey.com>
Reini Urban <······@x-ray.at> writes:

> Peter Seibel schrieb:
>> Ah, I see. So a better example would be ``(,@,'(list 1 2)). Then it
>> obviously does work, at least up to the step I showed before:
>>   CL-USER> ``(,@,'(list 1 2))
>>   (LIST 1 2)
>>   CL-USER> (eval *)
>>   (1 2)
>>   CL-USER> ``(,@,'(list 1 2) . nil)
>>   (LIST 1 2)
>>   CL-USER> (eval *)
>>   (1 2)
>>   CL-USER> `(append ,'(list 1 2) (quote nil))
>>   (APPEND (LIST 1 2) 'NIL)
>>   CL-USER> (eval *)
>>   (1 2)
>
> But please don't teach this in your book, unless you want to teach
> how to obfuscate in perl-spirit. Tricks are nice, but use them like
> Graham did, with proper macro names, explaining what they do, not
> just a ,@,X recipe.

Well, I wasn't planning to discuss this particular example. But,
unless I'm missing something, certain macro-writing macros such as
ONCE-ONLY (a.k.a. REBINDING) require--or at least are best
written--with nested backquotes. I was just trying to wrap my head
around the intricacies better.

-Peter

-- 
Peter Seibel                                      ·····@javamonkey.com

         Lisp is the red pill. -- John Fraser, comp.lang.lisp
From: Chris Riesbeck
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <criesbeck-F7C724.12443002122004@individual.net>
In article <··············@javamonkey.com>,
 Peter Seibel <·····@javamonkey.com> wrote:

> 
> Well, I wasn't planning to discuss this particular example. But,
> unless I'm missing something, certain macro-writing macros such as
> ONCE-ONLY (a.k.a. REBINDING) require--or at least are best
> written--with nested backquotes. I was just trying to wrap my head
> around the intricacies better.

Graham's On Lisp has a chapter on macros defining macros
and gives a step by step constructive method that, at
the time, helped me a lot. Looking at it again, it doesn't
seem quite as extensive as I recalled but still worth
a read or re-read. Available as a PDF file at

http://www.paulgraham.com/onlisptext.html
From: Bruno Haible
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <con4h2$odi$1@laposte.ilog.fr>
Peter Seibel <·····@javamonkey.com> wrote:
> unless I'm missing something, certain macro-writing macros such as
> ONCE-ONLY (a.k.a. REBINDING) require--or at least are best
> written--with nested backquotes. I was just trying to wrap my head
> around the intricacies better.

It is because of these intricacies that I would recommend to avoid
nested backquotes. When I encounter code using nested backquotes, I
usually have to think a long time about "what is this code trying to
achieve". This means, such code is not easy to maintain.

There are two ways to achieve conversion from doubly-nested backquote
to simple backquote: 1. use LIST, APPEND, etc. in the inner level,
instead of backquote. 2. use an auxiliary function which uses backquote.

As an example, take ONCE-ONLY.

Original code:

(defmacro once-only (variables &rest body)
  (assert (every #'symbolp variables))
  (let ((temps nil))
    (dotimes (i (length variables)) (push (gensym) temps))
    `(if (every #'side-effect-free? (list .,variables))
       (progn .,body)
       `(let
          (,,@(mapcar #'(lambda (tmp var)
                          ``(,',tmp ,,var))
                      temps variables))
          ,(let ,(mapcar #'(lambda (var tmp) `(,var ',tmp))
                         variables temps)
             .,body)))))

Using LIST etc. in the inner level:

(defmacro once-only (variables &rest body)
  (assert (every #'symbolp variables))
  (let ((temps nil))
    (dotimes (i (length variables)) (push (gensym) temps))
    `(if (every #'side-effect-free? (list .,variables))
       (progn .,body)
       (list 'let
         (list ,@(mapcar #'(lambda (tmp var)
                             `(list ',tmp ,var))
                         temps variables))
         (let ,(mapcar #'(lambda (var tmp) `(,var ',tmp))
                       variables temps)
           .,body)))))

Using auxiliary functions:

(defun construct-binding (variable form)
  `(,variable ,form))

(defun construct-let-wrapper (bindings body-form)
  `(let ,bindings ,body-form))

(defmacro once-only (variables &rest body)
  (assert (every #'symbolp variables))
  (let ((temps nil))
    (dotimes (i (length variables)) (push (gensym) temps))
    `(if (every #'side-effect-free? (list .,variables))
       (progn .,body)
       (construct-let-wrapper
         (list ,@(mapcar #'(lambda (tmp var)
                             `(construct-binding ',tmp ,var))
                         temps variables))
         (let ,(mapcar #'(lambda (var tmp) `(,var ',tmp))
                       variables temps)
           .,body)))))

I think both of the alternatives that avoid nested backquote are more
understandable than the original one.

               Bruno
From: Paul F. Dietz
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <Ad-dnah6M4sLtTLcRVn-pQ@dls.net>
Bruno Haible wrote:

> It is because of these intricacies that I would recommend to avoid
> nested backquotes. When I encounter code using nested backquotes, I
> usually have to think a long time about "what is this code trying to
> achieve". This means, such code is not easy to maintain.
> 
> There are two ways to achieve conversion from doubly-nested backquote
> to simple backquote: 1. use LIST, APPEND, etc. in the inner level,
> instead of backquote. 2. use an auxiliary function which uses backquote.

I second this recommendation.  Nested backquotes give me heartburn.

	Paul
From: Kaz Kylheku
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <cf333042.0412011101.715d2bf3@posting.google.com>
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> Take this backquoted expression:
> 
>   ``(,@,'(1 2 3))

There is a problem here, because 
> All the Common Lisp's I've tried it in agree that it evaluates to (1 2 3). E.g.

>   CL-USER> ``(,@,'(1 2 3))
>   (1 2 3)

However, note that this is just one round of evaluation. A double
backquote is intended for two rounds of evaluation, which leads to the
error that 1 is not a function!

This error is consistent with the observation that the argument to the
,@ operator must be an /expression/ that evaluates to a list. In this
case, the argument to the ,@ operator is the result of reducing ,'(1 2
3) or in other words the list expression (1 2 3).

So you have a problem here, it just happens not to show up in your
implementations until late.

> I'm trying to understand that by expanding it according to the rules
> in Section 2.4.6.
> 
> That section says when backquotes are nested, the innermost backquote
> expression is to be expanded first. So first I follow the rule that
> says:
> 
>  `(x1 x2 ... xn) === `(x1 x2 ... xn . nil)
> 
> to get this:
> 
>   CL-USER> ``(,@,'(1 2 3) . nil)
>   (1 2 3)
> 
> So far, so good. Now the appropriate rule to apply to the inner
> backquoted expression seems to be:
> 
>   `(x1 x2 x3 ... xn . atom) may be interpreted to mean
>     (append [ x1] [ x2] [ x3] ... [ xn] (quote atom))

So you get

`(append [,@',(1 2 3)])

Where the [,@form] is interpreted as form, so in other words:

`(append ',(1 2 3))

And now we are onto the next round of expansion, where ',(1 2 3) is
just an ordinary form, and so becomes (list `form), and the append
becomes `append:

(append (list `append) (list `',(1 2 3)))

And now these backquotes are expanded. The `append becomes 'append,
and the `',(1 2 3) ... like this. First let's rewrite it to make the
quote explicit:

(append (list `append) (list `(quote ,(1 2 3)))

Now:

(append (list 'append) (list (append (list `quote) (list (1 2 3)))))

The only backquote that now remains is to reduce `quote to 'quote:

(append (list 'append) (list (append (list 'quote) (list (1 2 3)))))

When we evaluate this, we get ``1 is not a function name'' in the very
first round.

In an optimized implementation, the code may be transformed such that
the problem does not show up until a second evaluation.
From: Justin Dubs
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <2e262238.0412011239.7ef10671@posting.google.com>
Peter Seibel <·····@javamonkey.com> wrote in message news:<··············@javamonkey.com>...
> Take this backquoted expression:
> 
>   ``(,@,'(1 2 3))
> 
> All the Common Lisp's I've tried it in agree that it evaluates to (1 2 3). E.g.
> 
>   CL-USER> ``(,@,'(1 2 3))
>   (1 2 3)

I'm no back-quote guru, but this doesn't seem right.  You have entered
an expression which is quoted twice.  One pass through eval can not
take them both away.  In LispWorks this evaluates as:

CL-USER 1 > ``(,@,'(1 2 3))
(system::bq-append (1 2 3))

If you did manage to get it evaluated twice, you'd have big problems. 
Your list, '(1 2 3) is only quoted once.  So, the innermost comma will
evaluate it once producing (1 2 3).  Then, you're left with ,@(1 2 3).
 The comma-at will attempt to evaluate (1 2 3) and you'll be left with
an error because 1 is not a symbol naming a function or a lambda
expression.

If you evaluate ``(,@,''(1 2 3)) twice, you'll get what you want.

The expansion flows as follows:

Initial:
``(,@,''(1 2 3))

After the `(x y z) -> `(x y z . nil) rule:
``(,@,''(1 2 3) . nil)

After the `(x y z . atom) -> (append ...) rule:
`(append ,''(1 2 3) `nil)

After the `(x y z) -> `(x y z . nil) rule:
`(append ,''(1 2 3) `nil . nil)

After the `(x y z . atom) -> (append ...) rule:
(append (list `append) (list ''(1 2 3)) (list ``nil) 'nil)

After a few of the `atom -> 'atom rules:
(append (list 'append) (list ''(1 2 3)) (list ''nil) 'nil)

Evaluating this once produces:
(append '(1 2 3) 'nil)

Evaluating it again produces:
(1 2 3)

Or, perhaps I'm missing something.  That happens a lot to me around
here...

Justin Dubs
From: Jens Axel Søgaard
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <41adc0bd$0$276$edfadb0f@dread12.news.tele.dk>
Peter Seibel wrote:

> Take this backquoted expression:
> 
>   ``(,@,'(1 2 3))
> 
> All the Common Lisp's I've tried it in agree that it evaluates to (1 2 3). E.g.
> 
>   CL-USER> ``(,@,'(1 2 3))
>   (1 2 3)

Bawden's description of ,@, is:

   ,@,X   The value of X will appear as an expression
          in the intermediate quasiquotation and the
          value of that expression will be /spliced/
          into the final result

Thus ,@,'(1 2 3) will splice the result of evaluating '(1 2 3)
giving us:

    `(,@'(1 2 3))

Splicing the list gives us:

    (1 2 3)

This is slightly cheating, since you are interested in follwowing
the rules given in the spec.

Bawden's paper is very nice and worth a read:

      "Quasiquotation in Lisp":
      <http://www.linearity.org/bawden/ftp/pepm99.ps.gz>

-- 
Jens Axel Søgaard
From: Kaz Kylheku
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <cf333042.0412011641.27725f64@posting.google.com>
Jens Axel S�gaard <······@soegaard.net> wrote in message news:<·······················@dread12.news.tele.dk>...
> Peter Seibel wrote:
> 
> > Take this backquoted expression:
> > 
> >   ``(,@,'(1 2 3))
> > 
> > All the Common Lisp's I've tried it in agree that it evaluates to (1 2 
>  3). E.g.
> > 
> >   CL-USER> ``(,@,'(1 2 3))
> >   (1 2 3)
> 
> Bawden's description of ,@, is:
> 
>    ,@,X   The value of X will appear as an expression
>           in the intermediate quasiquotation and the
>           value of that expression will be /spliced/
>           into the final result
> 
> Thus ,@,'(1 2 3) will splice the result of evaluating '(1 2 3)
> giving us:
> 
>     `(,@'(1 2 3))

Not quite. If we imagine the nested backquote processing as being done
layer by layer, whereby we take out backquote at a time, and strip out
and substitute the corresponding splices and commas, then we firstly
get this:

   `(,@(1 2 3))

Because the value of ,'(1 2 3) or ,(QUOTE (1 2 3)) is (1 2 3).  An
easy mnemonic for this is that ,' ``cancel out''.

This reduced backquote now contains an illegal splice because (1 2 3)
is not an expression that evaluates to a list.
From: Jens Axel Søgaard
Subject: Re: Any backquote guru's out there.
Date: 
Message-ID: <41aeed51$0$214$edfadb0f@dread12.news.tele.dk>
Kaz Kylheku wrote:

> Jens Axel Søgaard <······@soegaard.net> wrote in message news:<·······················@dread12.news.tele.dk>...
>>Peter Seibel wrote:

>>>Take this backquoted expression:
>>>
>>>  ``(,@,'(1 2 3))
>>>
>>>All the Common Lisp's I've tried it in agree that it evaluates to (1 2 
>>
>> 3). E.g.
>>
>>>  CL-USER> ``(,@,'(1 2 3))
>>>  (1 2 3)
>>
>>Bawden's description of ,@, is:
>>
>>   ,@,X   The value of X will appear as an expression
>>          in the intermediate quasiquotation and the
>>          value of that expression will be /spliced/
>>          into the final result
>>
>>Thus ,@,'(1 2 3) will splice the result of evaluating '(1 2 3)
>>giving us:
>>
>>    `(,@'(1 2 3))
> 
> 
> Not quite. If we imagine the nested backquote processing as being done
> layer by layer, whereby we take out backquote at a time, and strip out
> and substitute the corresponding splices and commas, then we firstly
> get this:
> 
>    `(,@(1 2 3))
> 
> Because the value of ,'(1 2 3) or ,(QUOTE (1 2 3)) is (1 2 3).  An
> easy mnemonic for this is that ,' ``cancel out''.
> 
> This reduced backquote now contains an illegal splice because (1 2 3)
> is not an expression that evaluates to a list.

You are right. It still confuses me, that the implementations Peter
used didn't report the error, but just spat out (1 2 3).

-- 
Jens Axel Søgaard