title: Is there a way to mix a LISP's macro &optional and &key params?
body: <p>I want to define a LISP macro like <code>dolist</code> that lets me define an optional output argument. In the following case study, this macro will be called <code>doread</code>. It will read lines from a file and return the number of lines found that way.</p>
  
  <pre class="lang-lisp prettyprint-override"><code>(let ((lines 0))
    (doread (line file lines)
       ;; do something with line
       (incf lines)))
  </code></pre>
  
  <p>The problem is that getting that <code>lines</code> to work in the above macro</p>
  
  <p>I can do what I want with &amp;key , but not with &amp;optional &amp;key (and the &amp;key is needed since I want to control how a file is read; e.g with <code>read</code> or <code>read-line</code> or whatever). </p>
  
  <p>Now the following works <strong>BUT</strong> to works the wrong way.  Here the <code>out</code> argument has to be  a <code>&amp;key</code> and not a &amp;optional:</p>
  
  <pre class="lang-lisp prettyprint-override"><code>;; this way works... 
  
  (defmacro doread ((it f  &amp;key out (take #'read)) &amp;body body)
    "Iterator for running over files or strings."
    (let ((str (gensym)))
      `(with-open-file (,str f)
         (loop for ,it = (funcall ,take ,str nil)
               while ,it do
               (progn ,@body))
         ,out)))
  
  ;; lets me define something that reads first line of a file
  (defun para1 (f)
    "Read everything up to first blank line."
    (with-output-to-string (s)
      (doread (x f :take #'read-line)
        (if (equalp "" (string-trim '(#\Space #\Tab) x))
          (return)
          (format s "~a~%" x)))))
  
  (print (para1 sometime)) ; ==&gt; shows all up to first blank line
  </code></pre>
  
  <p>What I'd like to do is this is the following (note that <code>out</code> has now moved into <code>&amp;optional</code>:</p>
  
  <pre class="lang-lisp prettyprint-override"><code>(defmacro doread ((it f &amp;optional out &amp;key   (take #'read)) &amp;body body)
    "Iterator for running over files or strings."
    (let ((str (gensym)))
      `(with-open-file (,str f)
         (loop for ,it = (funcall ,take ,str nil)
               while ,it do
               (progn ,@body))
         ,out)))
  </code></pre>
  
  <p>and if that worked, I could do something like.</p>
  
  <pre class="lang-lisp prettyprint-override"><code>(defun para1 (f)
    "Print  everything up to first blank line. 
     Return the lines found in that way"
    (let ((lines 0))
        (doread (x f lines :take #'read-line)
          (if (equalp "" (string-trim '(#\Space #\Tab) x))
              (return)
              (and (incf lines) (format t "~a~%" x)))))
  </code></pre>
  
  <p>but it I use <code>&amp;optional out</code> I get</p>
  
  <pre><code> loading /Users/timm/gits/timm/lisp/src/lib/macros.lisp
  *** - GETF: the property list (#'READ-LINE) has an odd length
  </code></pre>