mapcan/mapcon (new test)
Functions mapcan
and mapcon
have two edge cases where most implementations make mistakes.
(Including ccl, abcl, ecl, cmucl and clisp;
sbcl just fixed that on the mainstream;
clasp handles it right.)
Per http://www.lispworks.com/documentation/HyperSpec/Body/f_mapc_.htm
(mapcon f x1 ... xn)
should be equivalent to (apply #'nconc (maplist f x1 ... xn))
and similarly (mapcan f x1 ... xn)
to (apply #'nconc (mapcar f x1 ... xn))
.
-
All but last arguments to nconc must be proper lists, so
(nconc 1 2 3)
should error.(mapcan #'identity '(1 2 3))
and(mapcon #'car '(1 2 3))
are equivalent to(nconc 1 2 3)
, so they should error too, but the mentioned implementations return3
instead. -
nconcing lists with shared structure is valid (there is an example in specification: http://www.lispworks.com/documentation/HyperSpec/Body/f_nconc.htm), so it should be valid to do in
mapcan
andmapcon
as well. Here is an example where the mentioned implementations go into an infinite loop:
(setf *print-circle* t)
;; This returns #1=(1 2 . #1#)
(let ((lst (list 1 2)))
(flet ((f (x)
(declare (ignore x))
lst))
(apply #'nconc (mapcar #'f '(1 2)))))
;; Should be equivalent to the previous example, but hangs instead.
(let ((lst (list 1 2)))
(flet ((f (x)
(declare (ignore x))
lst))
(mapcan #'f '(1 2))))