From: Juanjo
Subject: #. and #=/## interaction
Date: 
Message-ID: <f9aa0bce-d052-40a3-9b58-267c564a1c95@j22g2000hsf.googlegroups.com>
Dear lispers,

I wanted to ask you all about your expectations regarding how the
reader macros #. and #= interact.

ECL as of CVS allows using #n=... and #n# inside the form read and
evaluated by #., but these objects are defined in a different context
than the form that encloses the evaluated one #.

So, say, the following works
#.(list '#1=(1 2) '#1#)
but the following will signal an error
'(#1=(1 2) #.(list '#1# #1#))

My interpretation of the specification is that it is very vague. It
explicitely says that reader macros _may_ call the reader recursively,
but I do not see an obligation. Furthermore, evaluation has to happen
right after the #. form is read. And finally, if we allow outside #=
macros to have an effect inside the evaluated form we may see problems
and system crashes with invalid forms such as
#1=(1 2 #.(list #1#))

Any opinions on this?

Juanjo

From: Pascal J. Bourguignon
Subject: Re: #. and #=/## interaction
Date: 
Message-ID: <7c1w02pyl5.fsf@pbourguignon.anevia.com>
Juanjo <·····················@googlemail.com> writes:

> Dear lispers,
>
> I wanted to ask you all about your expectations regarding how the
> reader macros #. and #= interact.
>
> ECL as of CVS allows using #n=... and #n# inside the form read and
> evaluated by #., but these objects are defined in a different context
> than the form that encloses the evaluated one #.
>
> So, say, the following works
> #.(list '#1=(1 2) '#1#)
> but the following will signal an error
> '(#1=(1 2) #.(list '#1# #1#))
>
> My interpretation of the specification is that it is very vague. It
> explicitely says that reader macros _may_ call the reader recursively,
> but I do not see an obligation. Furthermore, evaluation has to happen
> right after the #. form is read. And finally, if we allow outside #=
> macros to have an effect inside the evaluated form we may see problems
> and system crashes with invalid forms such as
> #1=(1 2 #.(list #1#))
>
> Any opinions on this?

I think #= and ## shouldn't cross execution times. 

#. introduces another round of read/macroexpand/compile/run time, so
it should not read recursively, and therefore references inside the
form read by #. shouldn't be merged with references outside.

As you noticed, doing otherwise may lead to try to execute a form
before it's entirely read.
          
-- 
__Pascal Bourguignon__
From: Juanjo
Subject: Re: #. and #=/## interaction
Date: 
Message-ID: <5553df81-8e13-444b-ae53-88db579ad692@z72g2000hsb.googlegroups.com>
On Sep 2, 1:38 pm, ····@informatimago.com (Pascal J. Bourguignon)
wrote:
> I think #= and ## shouldn't cross execution times.
> #. introduces another round of read/macroexpand/compile/run time, so
> it should not read recursively, and therefore references inside the
> form read by #. shouldn't be merged with references outside.

That is indeed my opinion. I am simply worried that there may be code
relying on the other behavior. Typing
'#.(list '#1=(1 2) '#1#)
'(#1=(1 2) #.(list '#1# '#1#))
'#1=(1 2 #.(list #1#))
with SBCL and CLISP gives
((1 2) (1 2))
((1 2) ((1 2) (1 2)))
and an infinite loop, respectively. In any case, the different
behavior of ECL (*) will be easily spotted, because ECL will return
((1 2) (1 2)) in the first case and signal the appropriate error in
the last two.

Juanjo

(*) I insist this is with the CVS version.
From: Kaz Kylheku
Subject: Re: #. and #=/## interaction
Date: 
Message-ID: <20080902163949.623@gmail.com>
On 2008-09-02, Pascal J. Bourguignon <···@informatimago.com> wrote:
> Juanjo <·····················@googlemail.com> writes:
>
>> Dear lispers,
>>
>> I wanted to ask you all about your expectations regarding how the
>> reader macros #. and #= interact.
>>
>> ECL as of CVS allows using #n=... and #n# inside the form read and
>> evaluated by #., but these objects are defined in a different context
>> than the form that encloses the evaluated one #.
>>
>> So, say, the following works
>> #.(list '#1=(1 2) '#1#)
>> but the following will signal an error
>> '(#1=(1 2) #.(list '#1# #1#))
>>
>> My interpretation of the specification is that it is very vague. It
>> explicitely says that reader macros _may_ call the reader recursively,
>> but I do not see an obligation. Furthermore, evaluation has to happen
>> right after the #. form is read. And finally, if we allow outside #=
>> macros to have an effect inside the evaluated form we may see problems
>> and system crashes with invalid forms such as
>> #1=(1 2 #.(list #1#))
>>
>> Any opinions on this?
>
> I think #= and ## shouldn't cross execution times. 
>
> #. introduces another round of read/macroexpand/compile/run time, so
> it should not read recursively, and therefore references inside the
> form read by #. shouldn't be merged with references outside.
>
> As you noticed, doing otherwise may lead to try to execute a form
> before it's entirely read.

I don't see a problem with this other than vagueness in the spec.

Some impelmentations probably just invoke a non-recursive read
(recursive-read-p is false), so that the form which follows #. is treated as a
toplevel form, unrelated to any surroudning material. Any #n# notations inside
that form are thus dangling.

But suppose that the implementation treats #n# notation by inserting marker
objects which are then located and backpatched, and suppose that #. does
perform a recursive read.  Since #. has to be called when the #. notation is
encountered, the form is invoked with these marker objects appearing in
argument material. The markers pass through its bowels and end up in the
output.  The output is substituted in place, markers and all, and processing
merrily continues. The backpatching then works over the substituted material
just as as well as over literal material.

Obviously, some things re not going to work. If the marker objects are atoms,
then some code will blow up, even if the final substituion calls for them to be
replaced by conses. E.g:

   #1=(#.(length '#1#))

Here, #1# is temporarily represented by some #<MARKER 1> thing, which is passed
to LENGTH, causing it to barf. 

But many uses, such as simple constructions, could work, for instance:

  #1=#.(LIST #1)

Here LIST gets #<MARKER 1>, and so produces (#<MARKER 1>).  This is now
backpatched, and reduces to #1=(#1). 

The concept does rationally hang together.
From: Pascal J. Bourguignon
Subject: Re: #. and #=/## interaction
Date: 
Message-ID: <87tzcyvtfy.fsf@hubble.informatimago.com>
Kaz Kylheku <········@gmail.com> writes:

> But suppose that the implementation treats #n# notation by inserting marker
> objects which are then located and backpatched, and suppose that #. does
> perform a recursive read.  Since #. has to be called when the #. notation is
> encountered, the form is invoked with these marker objects appearing in
> argument material. The markers pass through its bowels and end up in the
> output.  The output is substituted in place, markers and all, and processing
> merrily continues. The backpatching then works over the substituted material
> just as as well as over literal material.

'(#1=sin #2=42 = #.(#1# #2#))

> Obviously, some things re not going to work.

Yes.  This is a construction that doesn't work.  Better specify that
#. doesn't read its form recursively and have it work always.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

"Specifications are for the weak and timid!"