Re: or and values

Steve Haflich <[email protected]> Tue, 17 Oct 2006 14:06:52 -0700
Newsgroups gmane.lisp.allegro
Message-ID <8218.1161119212@bach>
   From: Harley Gorrell <[email protected]>
   
       I think that was done so implementations could have a
   simple "or".  You would get a better response from c.l.l.

"Simple" is not quite the right consideration.  Dropping multiple
values from various compound forms is an important efficiency issue.
Note that your macro below must cons a multiple values list in a way
that most implementations cannot stack cons.

The original poster's reluctance

  Is there a way to get all the values of the first form returning a non
  nil primary value? (without using conditionals and local variables)

to use conditional and temporary values is not really where the
proiblem is, since lexical variables in CL do not have any intrinsic
cost and merely serve to name a value that flows from a computational
source to a computational sink.  Conditionals are also not an issue,
since _some_ choice has to be made by the form, and the conditionals
are safely hidden from the user programmer's eyes inside the
macroexpansion.  However, consing a list of arbitrary unknown length
may have performance cost.  Note the multiple-value-list in the macro
below
   
       Not that I know of.  One can be written though.
   
   (defmacro or-mvl (&rest args)
      (or-mvl-1 args))
   
   (defun or-mvl-1 (args)
      (let ((arg (car args))
            (sym (gensym)))
        `(let ((,sym (multiple-value-list ,arg)))
           (if (car ,sym)
             (values-list ,sym)
             ,(if (cdr args)
                (or-mvl-1 (cdr args))
                nil)))))

This can be avoided, but only with real gymnastics that likely has a
different performance cost.  I believe the following will compile cons
free in ACL under sufficient optimization.

(defmacro ormv (&rest forms)
  (let ((blk (gensym))
	(fnc (gensym)))
    `(block ,blk
       (flet ((,fnc (&rest args)
		(declare (dynamic-extent args)
			 (optimize speed (safety 0)))
		(and args
		     (car args)
		     (return-from ,blk (apply #'values args)))))
	 (declare (dynamic-extent ,fnc))
	 ,@(mapcar (lambda (form)
		     `(multiple-value-call #'fnc ,form))
		   forms)))))

It exploits the ability of the compiler to stack cons a &rest list to
avoid heap consing, but it does have the overhead of establishing a
catch on the stack (to implement the block/return-from).  This is cons
free, but does cost cycles...