Comment 2 for bug 707573

Revision history for this message
Douglas Katzman (dougk) wrote :

We had a performance issue due to this in QPX, so have been using a shadowing DS-BIND which handles just enough to make our code work, but falls back to CL:DS-BIND if there are keyword arguments, which we never use in inner loops.
We expand like this:

* (macroexpand '(destructuring-bind (foo bar mum . baz) item (frob-it baz bar foo mum)))
(LET* ((#:G2459 ITEM)
       (FOO (POP #:G2459))
       (BAR (POP #:G2459))
       (MUM (NTH 0 #:G2459))
       (BAZ (NTHCDR 1 #:G2459)))
  (FROB-IT BAZ BAR FOO MUM))

I present the above, as I was about to report another issue about DS-BIND, but found that because of at least 4 open complaints, you probably don't want another. But essentially this bug is better served by an expansion that depends on current compilation policy. In our code we we have DS-BIND in inner loops, so not only is the extra CDRing bad, the list-of-length-p is pessimal also.
Mind you we also have code that thinks that (SIXTH (TENTH x)) is a perfectly reasonable and sane way to access a data structure, and doesn't want to first check that it's a list of 10.

Also, the reason for 'NTH' and 'NTHCDR' above is that sometimes we use a 'nil' as a dont-care value, in something like:
* (macroexpand '(destructuring-bind (foo bar nil nil nil mum . baz) item (frob-it baz bar foo mum)))
(LET* ((#:G2460 ITEM)
       (FOO (POP #:G2460))
       (BAR (POP #:G2460))
       (MUM (CAR (SETQ #:G2460 (NTHCDR 3 #:G2460))))
       (BAZ (NTHCDR 1 #:G2460)))
  (FROB-IT BAZ BAR FOO MUM))

I couldn't figure out if that was technically legal, but we do it, and in LOOP (which is deliberately not the same), it is legal.
One interpretation is that NIL is an empty nested destructuring lambda list, which could simply be ignored in unsafe code. I found that 3 Lisp implementations differed on what nil means in this context - choke, require a nil as the input for that place, or shutup and do nothing. I would like to know, if indeed I finish our homebrew implementation to submit back upstream.

The other issue, a genuine bug, not that it belongs here, is unexpectedly out-of-order evaluation of supplied-p variables.
Try this example and see why it might matter:
    (macroexpand '(cl:destructuring-bind (foo bar baz &key (a (not *foo-active-p*)) (b (compute-it) *foo-active-p*)) item (body)))