[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Compromise proposal for dotted-list functionality
OK, sentiment is clearly against considering everything to be a list. I
thought it was really simple and clean; but there you go. Most arguments
against it are usually of the form "people never use non-nil atoms as
lists, so list-processing functions should warn them by raising an error when
they do so by accident." I can see the point of this.
As my last shot in that argument, I will simply say that while it does
seem ridiculous to ever want to apply REVERSE to 7, I think it is very
important to handle boundary cases in a manner which is *consistent* with
the non-boundary cases. OK, now I will shut up.
Let me present a compromise that I think will preserve maximum ability to use
dotted lists when so desired and still catch errors due to programmers
inadvertently switching atoms and lists. It is essentially what Common Lisp
Common Lisp handles this tension by defining dotted lists, but in a slightly
odd way. In Common Lisp, dotted lists are defined to rule out non-nil atoms.
That is, these are dotted lists
(a b c . d)
(b c . d)
(c . d)
But this is not
In other words, *there are no zero-length dotted lists in Common Lisp*.
Doing so rules out all the non-intuitive boundary cases for list-processing
that everyone has been so freaked out about. Unfortunately, you can get a
measure for what a kludge this is by trying to construct a grammar that
defines the Common Lisp dotted list.
What we will do is allow dotted lists, but explicitly rule out the
non-nil atoms. So you can apply FIND or FILTER to
(a b c . 3)
if you really want to, but not
Below, I have a careful taxonomy of all the functions in the lib with this
new design criteria. Many functions continue to replicate the terminating
value of their input list, but they now will not accept non-nil atoms as
parameters. People who never, ever use dotted lists now will not get bitten
by goofing up argument order or other such errors that the earlier definitions
would have silently accepted. People who do use dotted lists, still can.
How do people feel about this?
For every procedure, we must specify
- What it accepts
- What it produces (if it accepts dotted lists, does it produce them?)
Replicates the argument's terminating value; non-nil atoms not allowed:
list-copy [e.g., (list-copy '(a b . c)) => (a b . c)]
filter partition remove
filter! partition! remove!
del delq delv delete
del! delq! delv! delete!
delq-duplicates delv-duplicates delete-duplicates del-duplicates
delq-duplicates! delv-duplicates! delete-duplicates! del-duplicates!
alist-delete del-ass del-assq del-assv del-assoc
alist-delete! del-ass! del-assq! del-assv! del-assoc!
mem member memq memv ass assoc assq assv
map filter-map map-in-order map! zip
-- These n-ary funcs use the terminator from left-most shortest list
arg. Simple to implement, and produces the desired result in the
single-list map case. Example:
(map + '(1 2 3 4 . a) '(1 0 1 . b) '(0 1 0 . c)) =>
(2 3 4 . b)
unzip2 unzip3 unzip4 unzip5
Termination of result not specified (may either replicate or nil-terminate);
non-nil atoms not allowed:
Produces nil-terminated result; non-nil atoms not allowed:
Handles improper lists; non-nil atoms not allowed:
fold fold-right pair-fold pair-fold-right reduce reduce-right
find any every list-index
Issue of nil-termination does not apply:
make-list list-tabulate list list*
null? pair? cons
car cdr ... cdddar cddddr set-car! set-cdr!
Handles improper-list args; non-empty lists of any kind not allowed:
first second third fourth fifth sixth seventh eighth ninth tenth
list-ref last last-pair
append append! reverse-append reverse-append!
Final argument may be anything.
Initial arguments may be improper lists; non-nil atoms not allowed.
These accept and produce exactly what
(apply append (apply map F LISTS))
(apply append! (apply map F LISTS))
The crux of the biscuit:
proper-list? dotted-list? circular-list?
General predicates -- may be applied to any value.
DOTTED-LIST? returns true on any non-pair. That is,
these three functions partition the entire value space.