[Openmcl-devel] #+/#- issues

Joshua TAYLOR tayloj at cs.rpi.edu
Tue Mar 3 18:15:07 PST 2009

Hi all,

 I browsed around on the Trac for a while to see if this has already
been mentioned, but I didn't find it (though there were things that
may be related; sorry if this is already documented/known/&c.). I was
recently reading some code that conditionally passed arguments to a
function, particularly, in DRAKMA's request.lisp I came across

  (usocket:socket-connect host port
    :element-type 'octet
    #+openmcl #+openmcl ;; this is what caught my eye
    :deadline deadline
    :nodelay t))

Now I had never seen that sort of conditionalization before, and so
before sending any bugreports here or to Edi I asked on c.l.l [1]
whether this adhered to the standard or not. As it turns out, it seems
that the behavior there is implementation-dependent. And I can confirm
that implementations do different things. E.g., CCL 1.3 does

(list #-ccl #-ccl 1 2 3 4) => (2 3 4)

while Lispworks 5.1 does

(list #-lispworks #-lispworks 1 2 3 4) => (3 4)

I think that more people might expect the latter behavior, but I think
that from the c.l.l discussion we determined that both are
permissible, depending on whether *READ-SUPPRESS* is bound to NIL or
to T when the second feature expression is being read. In fact, if we
push NIL onto *features* we can get some interesting results too.
Again, in CCL 1.3:

(push nil *features*) => (NIL :PRIMARY-CLASSES ...)
(list #-ccl #-ccl 1 2 3 4) => (3 4)

At any rate, that's all OK. I think that the following is a bug. In
the hyperspec we read:

" Sharpsign Minus
#- is like #+ except that it skips the expression if the test succeeds; that is,
#-test expression ==  #+(not test) expression"

But look what CCL does:

CL-USER> (list #-ccl #-ccl 1 2 3 4)
(2 3 4)
CL-USER> (list #-ccl #+(not ccl) 1 2 3 4)
(3 4)
CL-USER> (list #+(not ccl) #-ccl 1 2 3 4)
(2 3 4)
CL-USER> (list #+(not ccl) #+(not ccl) 1 2 3 4)
(3 4)

(Another interesting case is that (list #-ccl #+(not ccl) 2) returns
NIL, but (list #-ccl #-ccl 2) signals an error.)

Clearly #-ccl =/= #+(not ccl) in these examples. It seems that binding
*READ-SUPPRESS* to NIL during READ-FEATURE would get the behavior that
some people are expecting, and would make #-test and #+(not test)
behave equivalently. In l1-reader.lisp, that would be:

(defun read-feature (stream)
  (let* ((f (let* ((*package* *keyword-package*)
		   (*read-suppress* nil)) ; <== The added binding
              (read stream t nil t))))
    (labels ((eval-feature (form)
               (cond ((atom form)
                      (member form *features*))
                     ((eq (car form) :not)
                      (not (eval-feature (cadr form))))
                     ((eq (car form) :and)
                      (dolist (subform (cdr form) t)
                        (unless (eval-feature subform) (return))))
                     ((eq (car form) :or)
                      (dolist (subform (cdr form) nil)
                        (when (eval-feature subform) (return t))))
                     (t (%err-disp $XRDFEATURE form)))))
      (if (eval-feature f) #\+ #\-))))

Thanks all for a great Lisp! This is the only 'bug' I've run into so
far, and it hasn't even caused any problems for me yet. (But I were to
put NIL in *FEATURES*…)


[1] http://groups.google.com/group/comp.lang.lisp/browse_thread/thread/8d50284a29f51673#

Joshua Taylor
tayloj at cs.rpi.edu, jtaylor at alum.rpi.edu

"A lot of good things went down one time,
  back in the goodle days."
    John Hartford

More information about the Openmcl-devel mailing list