From: K. Ari Krupnikov
Subject: (rplaca) vs. (setf (car))
Date: 
Message-ID: <86brdqmehi.fsf@deb.lib.aero>
Is there any difference between these two forms? CLtL 15.3 would have
me believe that it's only a question of style, but I'm seeing
different behavior.

Here's a function that reads rfc1036 headers and returns a list of
header lines. If a line starts with whitespace, it is taken to be a
continuation of the previous line, and so is appended to the previous
line. This function is not supposed to be efficient, only a learning
tool.

With rplaca, it works as intended; with setf, I get a dotted list and
a subsequent error when I try to reverse it:

NREVERSE: A true list must not end with "some sting"

What am I missing?

(defun read-headers (stream)
  (do ((line (read-line stream nil 'eof)
	     (read-line stream nil 'eof))
       (headers '()
		(case (elt line 0)
		  ((#\Space #\Tab)
		   ;(rplaca headers
		   (setf (car headers)
			 (concatenate 'string (car headers)
				      (subseq line 1))))
		  (t (push line headers)))))
      ((or (eql line 'eof)
	   (equal line "."))
       (nreverse headers))))

What's even more puzzling for me is that setf expands to rplaca
(SYSTEM::%RPLACA in CLISP, SB-KERNEL:%RPLACA in SBCL).

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.

From: Edi Weitz
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <uk6sei5ji.fsf@agharta.de>
On 22 Nov 2004 01:15:21 -0800, ···@lib.aero (K. Ari Krupnikov) wrote:

> Is there any difference between these two forms? CLtL 15.3 would
> have me believe that it's only a question of style, but I'm seeing
> different behavior.
>
> Here's a function that reads rfc1036 headers and returns a list of
> header lines. If a line starts with whitespace, it is taken to be a
> continuation of the previous line, and so is appended to the
> previous line. This function is not supposed to be efficient, only a
> learning tool.
>
> With rplaca, it works as intended; with setf, I get a dotted list
> and a subsequent error when I try to reverse it:
>
> NREVERSE: A true list must not end with "some sting"
>
> What am I missing?
>
> (defun read-headers (stream)
>   (do ((line (read-line stream nil 'eof)
> 	     (read-line stream nil 'eof))
>        (headers '()
> 		(case (elt line 0)
> 		  ((#\Space #\Tab)
> 		   ;(rplaca headers
> 		   (setf (car headers)
> 			 (concatenate 'string (car headers)
> 				      (subseq line 1))))
> 		  (t (push line headers)))))
>       ((or (eql line 'eof)
> 	   (equal line "."))
>        (nreverse headers))))

The crucial difference is that RPLACA returns the CONS that was
changed while (SETF CAR) returns the element that was injected into
the cons.

  * (defparameter *a* (list 1 2 3))

  *A*
  * (rplaca *a* 42)

  (42 2 3)
  * (defparameter *a* (list 1 2 3))

  *A*
  * (setf (car *a*) 42)

  42

Since your DO loop relies on the return value and not only on the side
effect these are obviously two very different forms. What did you
expect?

> What's even more puzzling for me is that setf expands to rplaca
> (SYSTEM::%RPLACA in CLISP, SB-KERNEL:%RPLACA in SBCL).

That's an implementaion detail you shouldn't care about.

Cheers,
Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: K. Ari Krupnikov
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <86y8gtprmw.fsf@deb.lib.aero>
Edi Weitz <········@agharta.de> writes:

> The crucial difference is that RPLACA returns the CONS that was
> changed while (SETF CAR) returns the element that was injected into
> the cons.

Duh! Thank you.

Ari.

-- 
Elections only count as free and trials as fair if you can lose money
betting on the outcome.
From: Edi Weitz
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <ufz32i4zz.fsf@agharta.de>
On 22 Nov 2004 01:15:21 -0800, ···@lib.aero (K. Ari Krupnikov) wrote:

> Is there any difference between these two forms? CLtL 15.3 would
> have me believe that it's only a question of style, but I'm seeing
> different behavior.
>
> Here's a function that reads rfc1036 headers and returns a list of
> header lines. If a line starts with whitespace, it is taken to be a
> continuation of the previous line, and so is appended to the
> previous line. This function is not supposed to be efficient, only a
> learning tool.
>
> With rplaca, it works as intended; with setf, I get a dotted list
> and a subsequent error when I try to reverse it:
>
> NREVERSE: A true list must not end with "some sting"
>
> What am I missing?
>
> (defun read-headers (stream)
>   (do ((line (read-line stream nil 'eof)
>            (read-line stream nil 'eof))
>        (headers '()
>               (case (elt line 0)
>                 ((#\Space #\Tab)
>                  ;(rplaca headers
>                  (setf (car headers)
>                        (concatenate 'string (car headers)
>                                     (subseq line 1))))
>                 (t (push line headers)))))
>       ((or (eql line 'eof)
>          (equal line "."))
>        (nreverse headers))))

On second thought I think your main problem is that you haven't fully
understood how DO works. The STEP-FORM is intended to evaluate to the
new value of VAR, it is not meant to be a form that /sets/ the new
value of VAR. So, a "cleaner" version of your loop would probably be

  (do ((line (read-line stream nil 'eof)
           (read-line stream nil 'eof))
       (headers '()
                (case (elt line 0)
                  ((#\Space #\Tab)
                   (cons (concatenate 'string (car headers)
                                                   (subseq line 1))
                         (cdr headers)))
                  (t (cons line headers)))))
     ...

Or use LOOP instead.

Edi.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Jock Cooper
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <m3mzx9mzyq.fsf@jcooper02.sagepub.com>
Edi Weitz <········@agharta.de> writes:

> On 22 Nov 2004 01:15:21 -0800, ···@lib.aero (K. Ari Krupnikov) wrote:
> 
> > Is there any difference between these two forms? CLtL 15.3 would
> > have me believe that it's only a question of style, but I'm seeing
> > different behavior.
> >
> > Here's a function that reads rfc1036 headers and returns a list of
> > header lines. If a line starts with whitespace, it is taken to be a
> > continuation of the previous line, and so is appended to the
> > previous line. This function is not supposed to be efficient, only a
> > learning tool.
> >
> > With rplaca, it works as intended; with setf, I get a dotted list
> > and a subsequent error when I try to reverse it:
> >
> > NREVERSE: A true list must not end with "some sting"
> >
> > What am I missing?
> >
> > (defun read-headers (stream)
> >   (do ((line (read-line stream nil 'eof)
> >            (read-line stream nil 'eof))
> >        (headers '()
> >               (case (elt line 0)
> >                 ((#\Space #\Tab)
> >                  ;(rplaca headers
> >                  (setf (car headers)
> >                        (concatenate 'string (car headers)
> >                                     (subseq line 1))))
> >                 (t (push line headers)))))
> >       ((or (eql line 'eof)
> >          (equal line "."))
> >        (nreverse headers))))
> 
> On second thought I think your main problem is that you haven't fully
> understood how DO works. The STEP-FORM is intended to evaluate to the
> new value of VAR, it is not meant to be a form that /sets/ the new
> value of VAR. So, a "cleaner" version of your loop would probably be
> 
>   (do ((line (read-line stream nil 'eof)
>            (read-line stream nil 'eof))
>        (headers '()
>                 (case (elt line 0)
>                   ((#\Space #\Tab)
>                    (cons (concatenate 'string (car headers)
>                                                    (subseq line 1))
>                          (cdr headers)))
>                   (t (cons line headers)))))
>      ...
> 

If you are uncomfortable with the duplicated code you can use this 
handy idiom for the first form in the do:

(line #1=(read-line stream nil 'eof) #1#)
From: Szymon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <85653yuq8s.fsf@eva.rplacd.net>
···@lib.aero (K. Ari Krupnikov) writes:


> (defun read-headers (stream)
>   (do ((line (read-line stream nil 'eof)
> 	     (read-line stream nil 'eof))
>        (headers '()
> 		(case (elt line 0)
> 		  ((#\Space #\Tab)
> 		   ;(rplaca headers
> 		   (setf (car headers)
> 			 (concatenate 'string (car headers)
> 				      (subseq line 1))))
> 		  (t (push line headers)))))
>       ((or (eql line 'eof)
> 	   (equal line "."))
>        (nreverse headers))))

Btw, imho DO is not very good here:

;; NOT tested.

(loop with whitespace-list = '(#\Space #\Tab) and headers nil
      for line = (read-line stream nil)
      while (and line (not (string-equal line ".")))
      for first-char = (schar line 0)
      do (if (member first-char whitespace-list)
	     (setf (car headers)
		   (concatenate 'string (car headers) (subseq line 1)))
	   (push line headers)    
      finally (return (nreverse headers))))

Regards, Szymon.
From: Szymon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <851xemupqz.fsf@eva.rplacd.net>
Szymon <············@o2.pl> writes:

> ···@lib.aero (K. Ari Krupnikov) writes:
>
>
>> (defun read-headers (stream)
>>   (do ((line (read-line stream nil 'eof)
>> 	     (read-line stream nil 'eof))
>>        (headers '()
>> 		(case (elt line 0)
>> 		  ((#\Space #\Tab)
>> 		   ;(rplaca headers
>> 		   (setf (car headers)
>> 			 (concatenate 'string (car headers)
>> 				      (subseq line 1))))
>> 		  (t (push line headers)))))
>>       ((or (eql line 'eof)
>> 	   (equal line "."))
>>        (nreverse headers))))
>
> Btw, imho DO is not very good here:
>
> ;; NOT tested.
>
> (loop with whitespace-list = '(#\Space #\Tab) and headers nil
>       for line = (read-line stream nil)
>       while (and line (not (string-equal line ".")))
>       for first-char = (schar line 0)
>       do (if (member first-char whitespace-list)
> 	     (setf (car headers)


> 		   (concatenate 'string (car headers) (subseq line 1)))

Oh, my! Consing...

(concatenate 'string
             (car headers)
             (make-array (1- (length line))
                         :displaced-to line
                         :displaced-index-offset 1
                         :element-type 'character))


> 	   (push line headers)    
>       finally (return (nreverse headers))))

Regards, Szymon.
From: Szymon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <85wtwetakp.fsf@eva.rplacd.net>
Szymon <············@o2.pl> writes:

>> ;; NOT tested.
>>
>> (loop with whitespace-list = '(#\Space #\Tab) and headers nil
>>       for line = (read-line stream nil)
>>       while (and line (not (string-equal line ".")))
>>       for first-char = (schar line 0)
>>       do (if (member first-char whitespace-list)
>> 	     (setf (car headers)
>
>
>> 		   (concatenate 'string (car headers) (subseq line 1)))
>
> Oh, my! Consing...
>
> (concatenate 'string
>              (car headers)
>              (make-array (1- (length line))
>                          :displaced-to line
>                          :displaced-index-offset 1
>                          :element-type 'character))

or:

(let* ((header (car headers))
       (string (make-string (+ -1 (length line) (length header)))))
  (setf (subseq string 0) headers
        (subseq string (length header) line))
  string)

>
>
>> 	   (push line headers)    
>>       finally (return (nreverse headers))))



Regards, Szymon.
From: Szymon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <85sm72tae2.fsf@eva.rplacd.net>
Szymon <············@o2.pl> writes:


> (let* ((header (car headers))
>        (string (make-string (+ -1 (length line) (length header)))))
>   (setf (subseq string 0) headers
>         (subseq string (length header) line))
                                        ^^^^^^

Ermf... sorry... no. Forget this.

>   string)
From: Edi Weitz
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <u3bz2i1za.fsf@agharta.de>
On Mon, 22 Nov 2004 11:44:52 +0100, Szymon <············@o2.pl> wrote:

> Oh, my! Consing...
>
> (concatenate 'string
>              (car headers)
>              (make-array (1- (length line))
>                          :displaced-to line
>                          :displaced-index-offset 1
>                          :element-type 'character))

Oh, my! Premature optimization...

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Pascal Bourguignon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <877joeqgnz.fsf@thalassa.informatimago.com>
Edi Weitz <········@agharta.de> writes:

> On Mon, 22 Nov 2004 11:44:52 +0100, Szymon <············@o2.pl> wrote:
> 
> > Oh, my! Consing...
> >
> > (concatenate 'string
> >              (car headers)
> >              (make-array (1- (length line))
> >                          :displaced-to line
> >                          :displaced-index-offset 1
> >                          :element-type 'character))
> 
> Oh, my! Premature optimization...

In any case, I prefer, if it is needed to have a single concatenated
string as result, to do it at the end:

    (do ((chunks '())
         (line (read-string input nil nil) (read-string input nil nil)))
        ((null line)
         (apply (function concatenate) 'string (nreverse chunks)))
      (push line chunks))

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The world will now reboot; don't bother saving your artefacts.
From: Marcin 'Qrczak' Kowalczyk
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <87d5y6w1si.fsf@qrnik.zagroda>
Pascal Bourguignon <····@mouse-potato.com> writes:

> In any case, I prefer, if it is needed to have a single concatenated
> string as result, to do it at the end:
>
>     (do ((chunks '())
>          (line (read-string input nil nil) (read-string input nil nil)))
>         ((null line)
>          (apply (function concatenate) 'string (nreverse chunks)))
>       (push line chunks))

This will break if the number of chunks is larger than
call-arguments-limit, which can be as small as 50.

-- 
   __("<         Marcin Kowalczyk
   \__/       ······@knm.org.pl
    ^^     http://qrnik.knm.org.pl/~qrczak/
From: Pascal Bourguignon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <873bz2qejx.fsf@thalassa.informatimago.com>
Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:

> Pascal Bourguignon <····@mouse-potato.com> writes:
> 
> > In any case, I prefer, if it is needed to have a single concatenated
> > string as result, to do it at the end:
> >
> >     (do ((chunks '())
> >          (line (read-string input nil nil) (read-string input nil nil)))
> >         ((null line)
> >          (apply (function concatenate) 'string (nreverse chunks)))
> >       (push line chunks))
> 
> This will break if the number of chunks is larger than
> call-arguments-limit, which can be as small as 50.

What if I have a compiler macro that takes care of that?  Would it be possible?

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The world will now reboot; don't bother saving your artefacts.
From: Edi Weitz
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <uis7ygjxu.fsf@agharta.de>
On 22 Nov 2004 13:00:18 +0100, Pascal Bourguignon <····@mouse-potato.com> wrote:

> Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:
>
>> This will break if the number of chunks is larger than
>> call-arguments-limit, which can be as small as 50.
>
> What if I have a compiler macro that takes care of that?  Would it
> be possible?

I think this is forbidden by 11.1.2.1.2.

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Pascal Bourguignon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <87sm71pw0k.fsf@thalassa.informatimago.com>
Edi Weitz <········@agharta.de> writes:

> On 22 Nov 2004 13:00:18 +0100, Pascal Bourguignon <····@mouse-potato.com> wrote:
> 
> > Marcin 'Qrczak' Kowalczyk <······@knm.org.pl> writes:
> >
> >> This will break if the number of chunks is larger than
> >> call-arguments-limit, which can be as small as 50.
> >
> > What if I have a compiler macro that takes care of that?  Would it
> > be possible?
> 
> I think this is forbidden by 11.1.2.1.2.

Yes, CALL-ARGUMENTS-LIMIT applies (only) to functions:

    The upper exclusive bound on the number of arguments that may be
    passed to a function.

(I guess there would be no limit to the number of arguments given to a
_macro_).


So, I'll have to write it as:

;; not tested!

 (defun large-concat-str (strings)
    (if (<= (1+ (length strings)) call-arguments-limit)
        (apply (function concatenate) 'string strings)
        (do ((result (make-string (reduce (function +) 
                                   (mapcar (function length) strings))))
             (strings strings (cdr strings))
             (start 0))
            ((null strings) result)
        (replace result (car strings) :start1 start)
        (incf start (length (car strings))))))
    
    

  (do ((chunks '())
       (line (read-string input nil nil) (read-string input nil nil)))
      ((null line)  (large-concat-str (nreverse chunks)))
    (push line chunks))

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The world will now reboot; don't bother saving your artefacts.
From: Edi Weitz
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <uu0rhpuz6.fsf@agharta.de>
On 22 Nov 2004 19:40:43 +0100, Pascal Bourguignon <····@mouse-potato.com> wrote:

> Yes, CALL-ARGUMENTS-LIMIT applies (only) to functions:
>
>     The upper exclusive bound on the number of arguments that may be
>     passed to a function.
>
> (I guess there would be no limit to the number of arguments given to
> a _macro_).

A macro is defined by its macro /function/, isn't it?

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Dirk Gerrits
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <87pt25wtc9.fsf@dirkgerrits.com>
Edi Weitz <········@agharta.de> writes:

> On 22 Nov 2004 19:40:43 +0100, Pascal Bourguignon
> <····@mouse-potato.com> wrote:
>
>> Yes, CALL-ARGUMENTS-LIMIT applies (only) to functions:
>>
>>     The upper exclusive bound on the number of arguments that may be
>>     passed to a function.
>>
>> (I guess there would be no limit to the number of arguments given to
>> a _macro_).
>
> A macro is defined by its macro /function/, isn't it?

The glossary of the Hyperspec states:

 macro function n. a function of two arguments, a form and an
   environment, that implements macro expansion by producing a form to
   be evaluated in place of the original argument form.

I don't see any problem here, so if there is, please refer me to the
relevant Hyperspec section.

Kind regards,

Dirk Gerrits
From: Edi Weitz
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <uekilps5t.fsf@agharta.de>
On Mon, 22 Nov 2004 19:56:31 GMT, Dirk Gerrits <····@dirkgerrits.com> wrote:

> The glossary of the Hyperspec states:
>
>  macro function n. a function of two arguments, a form and an
>    environment, that implements macro expansion by producing a form to
>    be evaluated in place of the original argument form.
>
> I don't see any problem here, so if there is, please refer me to the
> relevant Hyperspec section.

Good point. So we're safe to write function bodies that span several
pages... :)

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Pascal Bourguignon
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <87fz31pumu.fsf@thalassa.informatimago.com>
Edi Weitz <········@agharta.de> writes:

> On 22 Nov 2004 19:40:43 +0100, Pascal Bourguignon <····@mouse-potato.com> wrote:
> 
> > Yes, CALL-ARGUMENTS-LIMIT applies (only) to functions:
> >
> >     The upper exclusive bound on the number of arguments that may be
> >     passed to a function.
> >
> > (I guess there would be no limit to the number of arguments given to
> > a _macro_).
> 
> A macro is defined by its macro /function/, isn't it?

Then we should be careful with all these &BODY arguments! :-(

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The world will now reboot; don't bother saving your artefacts.
From: Edi Weitz
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <ullctpsuf.fsf@agharta.de>
On 22 Nov 2004 20:10:33 +0100, Pascal Bourguignon <····@mouse-potato.com> wrote:

> Then we should be careful with all these &BODY arguments! :-(

No, you should write smaller functions... :)

-- 

Lisp is not dead, it just smells funny.

Real email: (replace (subseq ·········@agharta.de" 5) "edi")
From: Szymon
Subject: [OT] Question about style, DO vs LOOP.
Date: 
Message-ID: <85oehqt80o.fsf_-_@eva.rplacd.net>
Pascal Bourguignon <····@mouse-potato.com> writes:

> In any case, I prefer, if it is needed to have a single concatenated
> string as result, to do it at the end:
>
>     (do ((chunks '())
>          (line (read-string input nil nil) (read-string input nil nil)))
>         ((null line)
>          (apply (function concatenate) 'string (nreverse chunks)))
>       (push line chunks))

Why not?

(apply #'concatenate 'string
       (loop for line = (read-string input nil) while input collect input))

I'm just curious...

Regards, Szymon.
From: Szymon
Subject: Re: [OT] Question about style, DO vs LOOP.
Date: 
Message-ID: <85k6set7qt.fsf@eva.rplacd.net>
Szymon <············@o2.pl> writes:


> (apply #'concatenate 'string
>        (loop for line = (read-string input nil) while input collect input))

LOL...

(apply #'concatenate 'string
       (loop for line = (read-string input nil) while line collect line))
From: Pascal Bourguignon
Subject: Re: [OT] Question about style, DO vs LOOP.
Date: 
Message-ID: <87wtweozvl.fsf@thalassa.informatimago.com>
Szymon <············@o2.pl> writes:

> Pascal Bourguignon <····@mouse-potato.com> writes:
> 
> > In any case, I prefer, if it is needed to have a single concatenated
> > string as result, to do it at the end:
> >
> >     (do ((chunks '())
> >          (line (read-string input nil nil) (read-string input nil nil)))
> >         ((null line)
> >          (apply (function concatenate) 'string (nreverse chunks)))
> >       (push line chunks))
> 
> Why not?
> 
> (apply #'concatenate 'string
>        (loop for line = (read-string input nil) while input collect input))
> 
> I'm just curious...

Well, usually the number of loop variables, the condition and the body
of the loop are more complicated and ressemble more a DFA than
anything else...  Of course, if it was that simple, your formulation
is more elegant.

-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
The world will now reboot; don't bother saving your artefacts.
From: Joerg Hoehle
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <ubrd63shn.fsf@users.sourceforge.net>
Szymon <············@o2.pl> writes:
> ;; NOT tested.

Indeed. loop WHILE may not appear before FOR clauses.
It's bitten me as well, from time to time.

The Iterate utility does not have this restriction.

> (loop with whitespace-list = '(#\Space #\Tab) and headers nil
>       for line = (read-line stream nil)
>       while (and line (not (string-equal line ".")))
>       for first-char = (schar line 0)

Regards,
	Jorg Hohle
Telekom/T-Systems Technology Center
From: Wade Humeniuk
Subject: Re: (rplaca) vs. (setf (car))
Date: 
Message-ID: <ZNood.14444$l65.6622@clgrps13>
Since I am a big fan of PROG/TAGBODY/GO.  Here is another
version.  The main difference is in the concatenation.  The
algorithm pops off the car, concatenates it and then pushs it
back on.

(defun read-headers (stream)
   (prog ((headers nil))
     next-line
     (let ((line (read-line stream nil nil)))
       (cond
        ((or (null line) (string= "." line))
         (return-from read-headers (nreverse headers)))
        ((member (char line 0) '(#\space #\tab) :test #'char=)
         (push (concatenate 'string (pop headers) (subseq line 1)) headers))
        (t (push line headers))))
     (go next-line)))

In file read-headers.txt

This is a file
with concatented lines
   when i add a leading space
.

CL-USER 9 > (with-open-file (stream "read-headers.txt")
               (read-headers stream))
("This is a file" "with concatented lines when i add a leading space")

CL-USER 10 >

Wade