* Predicate for true lists @ 2018-04-16 19:34 Basil L. Contovounesios 2018-06-04 12:12 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-04-16 19:34 UTC (permalink / raw) To: emacs-devel [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Add predicate 'list-true-p' --] [-- Type: text/x-diff, Size: 11132 bytes --] From 15a5e9f48543dc114c2a9fe69fabddd0d41ec24c Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Mon, 16 Apr 2018 17:47:22 +0100 Subject: [PATCH] Add predicate 'list-true-p' * lisp/subr.el (list-true-p): New function. * doc/lispref/lists.texi (List-related Predicates): * etc/NEWS: Mention it. * lisp/emacs-lisp/byte-opt.el (byte-optimize-if): * lisp/emacs-lisp/cl-macs.el (cl--make-usage-args): * lisp/org/ob-core.el (org-babel-insert-result): Use it. * lisp/format.el (format-proper-list-p): Remove. (format-annotate-single-property-change): Use list-true-p instead. * lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove. (ert--explain-equal-rec): Use list-true-p instead. * test/lisp/emacs-lisp/ert-tests.el (ert-test-proper-list-p): Move from here... * test/lisp/subr-tests.el (subr-tests--list-true-p): ...to here, mutatis mutandis. --- doc/lispref/lists.texi | 16 ++++++++++++ etc/NEWS | 4 +++ lisp/emacs-lisp/byte-opt.el | 3 +-- lisp/emacs-lisp/cl-macs.el | 2 +- lisp/emacs-lisp/ert.el | 22 ++++------------ lisp/format.el | 12 ++------- lisp/org/ob-core.el | 5 ++-- lisp/subr.el | 6 +++++ test/lisp/emacs-lisp/ert-tests.el | 42 ------------------------------- test/lisp/subr-tests.el | 18 +++++++++++++ 10 files changed, 55 insertions(+), 75 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 761750eb20..62c14e963a 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -153,6 +153,22 @@ List-related Predicates @end example @end defun +@defun list-true-p object +This function returns @code{t} if OBJECT is a true list, @code{nil} +otherwise. In addition to satistying @code{listp}, a true list is +neither circular nor dotted. + +@example +@group +(list-true-p '(1 2 3)) + @result{} t +@end group +@group +(list-true-p '(1 2 . 3)) + @result{} nil +@end group +@end example +@end defun @node List Elements @section Accessing Elements of Lists diff --git a/etc/NEWS b/etc/NEWS index 5aa92e2991..c20b9ade97 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -487,6 +487,10 @@ x-lost-selection-hooks, x-sent-selection-hooks \f * Lisp Changes in Emacs 27.1 ++++ +** New function 'list-true-p' returns t for true lists which are +neither circular nor dotted. + +++ ** New function assoc-delete-all. diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 3bc4c438d6..d62ee2b95c 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -982,8 +982,7 @@ byte-optimize-if ;; (if <test> <then> nil) ==> (if <test> <then>) (let ((clause (nth 1 form))) (cond ((and (eq (car-safe clause) 'progn) - ;; `clause' is a proper list. - (null (cdr (last clause)))) + (list-true-p clause)) (if (null (cddr clause)) ;; A trivial `progn'. (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form))) diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 9600230c07..2ed8347ba8 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -498,7 +498,7 @@ cl--make-usage-args ;; `&aux' args aren't arguments, so let's just drop them from the ;; usage info. (setq arglist (cl-subseq arglist 0 aux)))) - (if (cdr-safe (last arglist)) ;Not a proper list. + (if (not (list-true-p arglist)) (let* ((last (last arglist)) (tail (cdr last))) (unwind-protect diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index 32bb367cdb..4134511f5d 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el @@ -472,18 +472,6 @@ ert--should-error-handle-error ;; buffer. Perhaps explanations should be reported through `ert-info' ;; rather than as part of the condition. -(defun ert--proper-list-p (x) - "Return non-nil if X is a proper list, nil otherwise." - (cl-loop - for firstp = t then nil - for fast = x then (cddr fast) - for slow = x then (cdr slow) do - (when (null fast) (cl-return t)) - (when (not (consp fast)) (cl-return nil)) - (when (null (cdr fast)) (cl-return t)) - (when (not (consp (cdr fast))) (cl-return nil)) - (when (and (not firstp) (eq fast slow)) (cl-return nil)))) - (defun ert--explain-format-atom (x) "Format the atom X for `ert--explain-equal'." (pcase x @@ -498,12 +486,12 @@ ert--explain-equal-rec `(different-types ,a ,b) (pcase-exhaustive a ((pred consp) - (let ((a-proper-p (ert--proper-list-p a)) - (b-proper-p (ert--proper-list-p b))) - (if (not (eql (not a-proper-p) (not b-proper-p))) + (let ((a-proper-p (list-true-p a)) + (b-proper-p (list-true-p b))) + (if (not (eq (not a-proper-p) (not b-proper-p))) `(one-list-proper-one-improper ,a ,b) (if a-proper-p - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(proper-lists-of-different-length ,(length a) ,(length b) ,a ,b first-mismatch-at @@ -523,7 +511,7 @@ ert--explain-equal-rec (cl-assert (equal a b) t) nil)))))))) ((pred arrayp) - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(arrays-of-different-length ,(length a) ,(length b) ,a ,b ,@(unless (char-table-p a) diff --git a/lisp/format.el b/lisp/format.el index 2f198e3eb7..e5bc60712b 100644 --- a/lisp/format.el +++ b/lisp/format.el @@ -539,14 +539,6 @@ format-make-relatively-unique (setq tail next))) (cons acopy bcopy))) -(defun format-proper-list-p (list) - "Return t if LIST is a proper list. -A proper list is a list ending with a nil cdr, not with an atom " - (when (listp list) - (while (consp list) - (setq list (cdr list))) - (null list))) - (defun format-reorder (items order) "Arrange ITEMS to follow partial ORDER. Elements of ITEMS equal to elements of ORDER will be rearranged @@ -1005,8 +997,8 @@ format-annotate-single-property-change ;; If either old or new is a list, have to treat both that way. (if (and (or (listp old) (listp new)) (not (get prop 'format-list-atomic-p))) - (if (or (not (format-proper-list-p old)) - (not (format-proper-list-p new))) + (if (not (and (list-true-p old) + (list-true-p new))) (format-annotate-atomic-property-change prop-alist old new) (let* ((old (if (listp old) old (list old))) (new (if (listp new) new (list new))) diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index 5d5faaa6fd..f931bb3c31 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -2310,10 +2310,9 @@ org-babel-insert-result (lambda (r) ;; Non-nil when result R can be turned into ;; a table. - (and (listp r) - (null (cdr (last r))) + (and (list-true-p r) (cl-every - (lambda (e) (or (atom e) (null (cdr (last e))))) + (lambda (e) (or (atom e) (list-true-p e))) result))))) ;; insert results based on type (cond diff --git a/lisp/subr.el b/lisp/subr.el index 9cf7d596cd..4a0ab321bc 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -548,6 +548,12 @@ nbutlast (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil)) list)))) +(defun list-true-p (object) + "Return t if OBJECT is a true list. +A true list is neither circular nor dotted (i.e., its last `cdr' +is nil)." + (null (nthcdr (safe-length object) object))) + (defun zerop (number) "Return t if NUMBER is zero." ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el index e92b434274..cb957bd9fd 100644 --- a/test/lisp/emacs-lisp/ert-tests.el +++ b/test/lisp/emacs-lisp/ert-tests.el @@ -496,48 +496,6 @@ ert-test--which-file ;;; Tests for utility functions. -(ert-deftest ert-test-proper-list-p () - (should (ert--proper-list-p '())) - (should (ert--proper-list-p '(1))) - (should (ert--proper-list-p '(1 2))) - (should (ert--proper-list-p '(1 2 3))) - (should (ert--proper-list-p '(1 2 3 4))) - (should (not (ert--proper-list-p 'a))) - (should (not (ert--proper-list-p '(1 . a)))) - (should (not (ert--proper-list-p '(1 2 . a)))) - (should (not (ert--proper-list-p '(1 2 3 . a)))) - (should (not (ert--proper-list-p '(1 2 3 4 . a)))) - (let ((a (list 1))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cl-cdddr a)) - (should (not (ert--proper-list-p a))))) - (ert-deftest ert-test-parse-keys-and-body () (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo)))) (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil))) diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 52b61d9fb9..63fe7ee139 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -306,6 +306,24 @@ subr-test--frames-1 (should (eq (string-to-char (symbol-name (gensym))) ?g)) (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) +(ert-deftest subr-tests--list-true-p () + "Test `list-true-p' behavior." + (dotimes (length 4) + ;; True and dotted lists + (let ((list (make-list length 0))) + (should (list-true-p list)) + (should (not (list-true-p (nconc list 0))))) + ;; Circular lists + (dotimes (n (1+ length)) + (let ((circle (make-list (1+ length) 0))) + (should (not (list-true-p (nconc circle (nthcdr n circle)))))))) + ;; Atoms + (should (not (list-true-p 0))) + (should (not (list-true-p ""))) + (should (not (list-true-p []))) + (should (not (list-true-p (make-bool-vector 0 nil)))) + (should (not (list-true-p (make-symbol "a"))))) + (ert-deftest subr-tests--assq-delete-all () "Test `assq-delete-all' behavior." (cl-flet ((new-list-fn -- 2.17.0 [-- Attachment #2: Benchmark for 'list-true-p' --] [-- Type: application/emacs-lisp, Size: 873 bytes --] [-- Attachment #3: Type: text/plain, Size: 1757 bytes --] A while ago, I added a little convenience function to my init file[1] to determine whether a given object is a true list (as opposed to a circular or dotted one): (null (nthcdr (safe-length object) object)) [1]: https://github.com/basil-conto/dotfiles/blob/96aeb904a6fd94d9fbcf95483fd4f79194e90592/.emacs.d/lisp/blc-lib.el#L46-L49 Since then, I have noticed variations of this predicate/condition strewn around the Emacs sources, albeit usually implemented less efficiently. Would such a predicate be a welcome addition to, say, subr.el or subr-x.el? I attach a first draft of a patch targeting subr.el for your consideration. Also attached is a toy benchmark comparing the new function list-true-p with the existing format-proper-list-p and ert--proper-list-p when given, in turn, a true, dotted, and circular list as argument. It prints the following, after a couple of runs: ‘format-proper-list-p’ true (0.16966186900000002 0 0.0) dotted (0.168859839 0 0.0) circular (0.244791363 0 0.0) ‘ert--proper-list-p’ true (0.622797443 0 0.0) dotted (0.621622385 0 0.0) circular (0.9150398590000001 0 0.0) ‘list-true-p’ true (0.042970005000000006 0 0.0) dotted (0.04294060500000001 0 0.0) circular (0.057346661 0 0.0) P.S. What is the preferred way of formatting car/cdr in docstrings? The manuals seem to use small caps CAR/CDR and subr.el seems to alternate between no quotes and `car'/`cdr'. I have gone with the latter for the docstring of list-true-p, at least for now. Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-04-16 19:34 Predicate for true lists Basil L. Contovounesios @ 2018-06-04 12:12 ` Basil L. Contovounesios 2018-06-04 14:08 ` Stefan Monnier 2018-06-05 1:23 ` Paul Eggert 0 siblings, 2 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2018-06-04 12:12 UTC (permalink / raw) To: emacs-devel [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: Add predicate 'list-true-p' --] [-- Type: text/x-diff, Size: 11112 bytes --] From 4264c2d5bb40bc5ceb86acb07c0fdc41f6365399 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Mon, 4 Jun 2018 12:56:41 +0100 Subject: [PATCH] Add predicate 'list-true-p' * lisp/subr.el (list-true-p): New function. * doc/lispref/lists.texi (List-related Predicates): * etc/NEWS: Mention it. * lisp/emacs-lisp/byte-opt.el (byte-optimize-if): * lisp/emacs-lisp/cl-macs.el (cl--make-usage-args): * lisp/org/ob-core.el (org-babel-insert-result): Use it. * lisp/format.el (format-proper-list-p): Remove. (format-annotate-single-property-change): Use list-true-p instead. * lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove. (ert--explain-equal-rec): Use list-true-p instead. * test/lisp/emacs-lisp/ert-tests.el (ert-test-proper-list-p): Move from here... * test/lisp/subr-tests.el (subr-tests--list-true-p): ...to here, mutatis mutandis. --- doc/lispref/lists.texi | 16 ++++++++++++ etc/NEWS | 4 +++ lisp/emacs-lisp/byte-opt.el | 3 +-- lisp/emacs-lisp/cl-macs.el | 2 +- lisp/emacs-lisp/ert.el | 22 ++++------------ lisp/format.el | 12 ++------- lisp/org/ob-core.el | 5 ++-- lisp/subr.el | 6 +++++ test/lisp/emacs-lisp/ert-tests.el | 42 ------------------------------- test/lisp/subr-tests.el | 18 +++++++++++++ 10 files changed, 55 insertions(+), 75 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 761750eb20..62c14e963a 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -153,6 +153,22 @@ List-related Predicates @end example @end defun +@defun list-true-p object +This function returns @code{t} if OBJECT is a true list, @code{nil} +otherwise. In addition to satistying @code{listp}, a true list is +neither circular nor dotted. + +@example +@group +(list-true-p '(1 2 3)) + @result{} t +@end group +@group +(list-true-p '(1 2 . 3)) + @result{} nil +@end group +@end example +@end defun @node List Elements @section Accessing Elements of Lists diff --git a/etc/NEWS b/etc/NEWS index 1b324986d9..a357d8b0f0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -561,6 +561,10 @@ manual for more details. \f * Lisp Changes in Emacs 27.1 ++++ +** New function 'list-true-p' returns t for true lists which are +neither circular nor dotted. + +++ ** New function assoc-delete-all. diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 3bc4c438d6..d62ee2b95c 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -982,8 +982,7 @@ byte-optimize-if ;; (if <test> <then> nil) ==> (if <test> <then>) (let ((clause (nth 1 form))) (cond ((and (eq (car-safe clause) 'progn) - ;; `clause' is a proper list. - (null (cdr (last clause)))) + (list-true-p clause)) (if (null (cddr clause)) ;; A trivial `progn'. (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form))) diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index d7e4b4e611..8ad4c053db 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -498,7 +498,7 @@ cl--make-usage-args ;; `&aux' args aren't arguments, so let's just drop them from the ;; usage info. (setq arglist (cl-subseq arglist 0 aux)))) - (if (cdr-safe (last arglist)) ;Not a proper list. + (if (not (list-true-p arglist)) (let* ((last (last arglist)) (tail (cdr last))) (unwind-protect diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index 32bb367cdb..4134511f5d 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el @@ -472,18 +472,6 @@ ert--should-error-handle-error ;; buffer. Perhaps explanations should be reported through `ert-info' ;; rather than as part of the condition. -(defun ert--proper-list-p (x) - "Return non-nil if X is a proper list, nil otherwise." - (cl-loop - for firstp = t then nil - for fast = x then (cddr fast) - for slow = x then (cdr slow) do - (when (null fast) (cl-return t)) - (when (not (consp fast)) (cl-return nil)) - (when (null (cdr fast)) (cl-return t)) - (when (not (consp (cdr fast))) (cl-return nil)) - (when (and (not firstp) (eq fast slow)) (cl-return nil)))) - (defun ert--explain-format-atom (x) "Format the atom X for `ert--explain-equal'." (pcase x @@ -498,12 +486,12 @@ ert--explain-equal-rec `(different-types ,a ,b) (pcase-exhaustive a ((pred consp) - (let ((a-proper-p (ert--proper-list-p a)) - (b-proper-p (ert--proper-list-p b))) - (if (not (eql (not a-proper-p) (not b-proper-p))) + (let ((a-proper-p (list-true-p a)) + (b-proper-p (list-true-p b))) + (if (not (eq (not a-proper-p) (not b-proper-p))) `(one-list-proper-one-improper ,a ,b) (if a-proper-p - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(proper-lists-of-different-length ,(length a) ,(length b) ,a ,b first-mismatch-at @@ -523,7 +511,7 @@ ert--explain-equal-rec (cl-assert (equal a b) t) nil)))))))) ((pred arrayp) - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(arrays-of-different-length ,(length a) ,(length b) ,a ,b ,@(unless (char-table-p a) diff --git a/lisp/format.el b/lisp/format.el index 2f198e3eb7..e5bc60712b 100644 --- a/lisp/format.el +++ b/lisp/format.el @@ -539,14 +539,6 @@ format-make-relatively-unique (setq tail next))) (cons acopy bcopy))) -(defun format-proper-list-p (list) - "Return t if LIST is a proper list. -A proper list is a list ending with a nil cdr, not with an atom " - (when (listp list) - (while (consp list) - (setq list (cdr list))) - (null list))) - (defun format-reorder (items order) "Arrange ITEMS to follow partial ORDER. Elements of ITEMS equal to elements of ORDER will be rearranged @@ -1005,8 +997,8 @@ format-annotate-single-property-change ;; If either old or new is a list, have to treat both that way. (if (and (or (listp old) (listp new)) (not (get prop 'format-list-atomic-p))) - (if (or (not (format-proper-list-p old)) - (not (format-proper-list-p new))) + (if (not (and (list-true-p old) + (list-true-p new))) (format-annotate-atomic-property-change prop-alist old new) (let* ((old (if (listp old) old (list old))) (new (if (listp new) new (list new))) diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index 5d5faaa6fd..f931bb3c31 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -2310,10 +2310,9 @@ org-babel-insert-result (lambda (r) ;; Non-nil when result R can be turned into ;; a table. - (and (listp r) - (null (cdr (last r))) + (and (list-true-p r) (cl-every - (lambda (e) (or (atom e) (null (cdr (last e))))) + (lambda (e) (or (atom e) (list-true-p e))) result))))) ;; insert results based on type (cond diff --git a/lisp/subr.el b/lisp/subr.el index 914112ccef..7090053b5c 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -548,6 +548,12 @@ nbutlast (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil)) list)))) +(defun list-true-p (object) + "Return t if OBJECT is a true list. +A true list is neither circular nor dotted (i.e., its last `cdr' +is nil)." + (null (nthcdr (safe-length object) object))) + (defun zerop (number) "Return t if NUMBER is zero." ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el index e92b434274..cb957bd9fd 100644 --- a/test/lisp/emacs-lisp/ert-tests.el +++ b/test/lisp/emacs-lisp/ert-tests.el @@ -496,48 +496,6 @@ ert-test--which-file ;;; Tests for utility functions. -(ert-deftest ert-test-proper-list-p () - (should (ert--proper-list-p '())) - (should (ert--proper-list-p '(1))) - (should (ert--proper-list-p '(1 2))) - (should (ert--proper-list-p '(1 2 3))) - (should (ert--proper-list-p '(1 2 3 4))) - (should (not (ert--proper-list-p 'a))) - (should (not (ert--proper-list-p '(1 . a)))) - (should (not (ert--proper-list-p '(1 2 . a)))) - (should (not (ert--proper-list-p '(1 2 3 . a)))) - (should (not (ert--proper-list-p '(1 2 3 4 . a)))) - (let ((a (list 1))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cl-cdddr a)) - (should (not (ert--proper-list-p a))))) - (ert-deftest ert-test-parse-keys-and-body () (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo)))) (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil))) diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 52b61d9fb9..b7675ec54d 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -306,6 +306,24 @@ subr-test--frames-1 (should (eq (string-to-char (symbol-name (gensym))) ?g)) (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) +(ert-deftest subr-tests--list-true-p () + "Test `list-true-p' behavior." + (dotimes (length 4) + ;; True and dotted lists. + (let ((list (make-list length 0))) + (should (list-true-p list)) + (should (not (list-true-p (nconc list 0))))) + ;; Circular lists. + (dotimes (n (1+ length)) + (let ((circle (make-list (1+ length) 0))) + (should (not (list-true-p (nconc circle (nthcdr n circle)))))))) + ;; Atoms. + (should (not (list-true-p 0))) + (should (not (list-true-p ""))) + (should (not (list-true-p []))) + (should (not (list-true-p (make-bool-vector 0 nil)))) + (should (not (list-true-p (make-symbol "a"))))) + (ert-deftest subr-tests--assq-delete-all () "Test `assq-delete-all' behavior." (cl-flet ((new-list-fn -- 2.17.1 [-- Attachment #2: Benchmark for 'list-true-p' --] [-- Type: application/emacs-lisp, Size: 900 bytes --] [-- Attachment #3: Type: text/plain, Size: 1985 bytes --] "Basil L. Contovounesios" <contovob@tcd.ie> writes: > A while ago, I added a little convenience function to my init file[1] > to determine whether a given object is a true list (as opposed to a > circular or dotted one): > > (null (nthcdr (safe-length object) object)) > > [1]: https://github.com/basil-conto/dotfiles/blob/96aeb904a6fd94d9fbcf95483fd4f79194e90592/.emacs.d/lisp/blc-lib.el#L46-L49 > > Since then, I have noticed variations of this predicate/condition strewn > around the Emacs sources, albeit usually implemented less efficiently. > > Would such a predicate be a welcome addition to, say, subr.el or > subr-x.el? I attach a first draft of a patch targeting subr.el for your > consideration. > > Also attached is a toy benchmark comparing the new function list-true-p > with the existing format-proper-list-p and ert--proper-list-p when > given, in turn, a true, dotted, and circular list as argument. > It prints the following, after a couple of runs: > > ‘format-proper-list-p’ > true (0.16966186900000002 0 0.0) > dotted (0.168859839 0 0.0) > circular (0.244791363 0 0.0) > ‘ert--proper-list-p’ > true (0.622797443 0 0.0) > dotted (0.621622385 0 0.0) > circular (0.9150398590000001 0 0.0) > ‘list-true-p’ > true (0.042970005000000006 0 0.0) > dotted (0.04294060500000001 0 0.0) > circular (0.057346661 0 0.0) > > P.S. What is the preferred way of formatting car/cdr in docstrings? > The manuals seem to use small caps CAR/CDR and subr.el seems to > alternate between no quotes and `car'/`cdr'. I have gone with the > latter for the docstring of list-true-p, at least for now. Any interest in or comments on this proposal? Should I submit a wishlist bug report for this instead? Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-04 12:12 ` Basil L. Contovounesios @ 2018-06-04 14:08 ` Stefan Monnier 2018-06-04 14:46 ` Basil L. Contovounesios 2018-06-05 1:23 ` Paul Eggert 1 sibling, 1 reply; 100+ messages in thread From: Stefan Monnier @ 2018-06-04 14:08 UTC (permalink / raw) To: emacs-devel > +@defun list-true-p object > +This function returns @code{t} if OBJECT is a true list, @code{nil} > +otherwise. In addition to satistying @code{listp}, a true list is > +neither circular nor dotted. I think this function's true name is `list-proper-p` or `proper-list-p`. At least I've always heard it talked about as "a proper list vs a non-proper list". As for `cdr` vs CDR. The all-caps version is a metavariable referring to something which we happened to call CDR. E.g. Foo takes the form (CAR . CDR) where CDR is blabla. so I think your use of `cdr` was the right choice. Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-04 14:08 ` Stefan Monnier @ 2018-06-04 14:46 ` Basil L. Contovounesios 2018-06-04 15:31 ` Drew Adams 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-06-04 14:46 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: >> +@defun list-true-p object >> +This function returns @code{t} if OBJECT is a true list, @code{nil} >> +otherwise. In addition to satistying @code{listp}, a true list is >> +neither circular nor dotted. > > I think this function's true name is `list-proper-p` or `proper-list-p`. > At least I've always heard it talked about as "a proper list vs > a non-proper list". That was my impression as well, but "(elisp) Cons Cells" states: Also by convention, the CDR of the last cons cell in a list is ‘nil’. We call such a ‘nil’-terminated structure a “true list”. The documentation of functions 'append' in "(elisp) Building Lists" and 'vconcat' in "(elisp) Vector Functions" similarly refer to "true" lists. If "proper" is the more common/preferred term, I should probably update those nodes in addition to my patch, right? > As for `cdr` vs CDR. The all-caps version is a metavariable referring > to something which we happened to call CDR. E.g. > > Foo takes the form (CAR . CDR) where CDR is blabla. Right, but I see no such destructuring/metasyntax in the Elisp manual. Both "(elisp) Cons Cells" and "(elisp) Cons Cell Type", for example, refer directly to the CAR and CDR slots of a cons cell, without first illustrating their structure. Is that OK? Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-06-04 14:46 ` Basil L. Contovounesios @ 2018-06-04 15:31 ` Drew Adams 2018-06-04 16:14 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2018-06-04 15:31 UTC (permalink / raw) To: Basil L. Contovounesios, Stefan Monnier; +Cc: emacs-devel > > As for `cdr` vs CDR. The all-caps version is a metavariable referring > > to something which we happened to call CDR. E.g. > > > > Foo takes the form (CAR . CDR) where CDR is blabla. > > Right, but I see no such destructuring/metasyntax in the Elisp manual. > Both "(elisp) Cons Cells" and "(elisp) Cons Cell Type", for example, > refer directly to the CAR and CDR slots of a cons cell, without first > illustrating their structure. Is that OK? My answer probably does not reflect an Emacs doc-string or manual convention, but here are my 2 cents anyway: 1. Uppercase: As Stefan said, only when it refers to a piece of a pattern. 2. Lowercase in code font (e.g. what Stefan wrote as `cdr` and you wrote as `cdr'): Only when referring to the function. 3. Lowercase otherwise. In talking about Lisp, "car" and "cdr" are regular English words, just like "first" and "rest". They are Lisp jargon words, but they are English nevertheless. IOW, there's no reason we should not just talk about the cdr of a list, with no special typography used for the word "cdr". ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-04 15:31 ` Drew Adams @ 2018-06-04 16:14 ` Basil L. Contovounesios 2018-06-04 16:38 ` Drew Adams 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-06-04 16:14 UTC (permalink / raw) To: Drew Adams; +Cc: Stefan Monnier, emacs-devel Drew Adams <drew.adams@oracle.com> writes: >> > As for `cdr` vs CDR. The all-caps version is a metavariable referring >> > to something which we happened to call CDR. E.g. >> > >> > Foo takes the form (CAR . CDR) where CDR is blabla. >> >> Right, but I see no such destructuring/metasyntax in the Elisp manual. >> Both "(elisp) Cons Cells" and "(elisp) Cons Cell Type", for example, >> refer directly to the CAR and CDR slots of a cons cell, without first >> illustrating their structure. Is that OK? > > My answer probably does not reflect an Emacs doc-string or > manual convention, but here are my 2 cents anyway: > > 1. Uppercase: As Stefan said, only when it refers to a > piece of a pattern. > > 2. Lowercase in code font (e.g. what Stefan wrote as `cdr` > and you wrote as `cdr'): Only when referring to the function. > > 3. Lowercase otherwise. In talking about Lisp, "car" and > "cdr" are regular English words, just like "first" and "rest". > They are Lisp jargon words, but they are English nevertheless. Agreed. > IOW, there's no reason we should not just talk about the cdr > of a list, with no special typography used for the word "cdr". In the manual, yes. In docstrings, on the other hand, I like that `cdr' quoting results in a link to that function's documentation. -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-06-04 16:14 ` Basil L. Contovounesios @ 2018-06-04 16:38 ` Drew Adams 2018-06-04 16:57 ` Stefan Monnier 0 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2018-06-04 16:38 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: Stefan Monnier, emacs-devel > > IOW, there's no reason we should not just talk about the cdr > > of a list, with no special typography used for the word "cdr". > > In the manual, yes. In docstrings, on the other hand, I like that `cdr' > quoting results in a link to that function's documentation. OK, but only if the function is really what is meant. If all that is meant is the cons place/position or the return value from the function then I'd just say "the car" or "the cdr" (without the quotes). Same thing with talking about conses (aka "cons cells") - "cons" is a word, for Emacs users. Anyway, as mentioned, what I said "probably does not reflect an Emacs doc-string or manual convention". It's just my opinion: we shouldn't shy away from using (common, longstanding) Lispy language when talking about the Lisp language. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-04 16:38 ` Drew Adams @ 2018-06-04 16:57 ` Stefan Monnier 0 siblings, 0 replies; 100+ messages in thread From: Stefan Monnier @ 2018-06-04 16:57 UTC (permalink / raw) To: Drew Adams; +Cc: Basil L. Contovounesios, emacs-devel > Anyway, as mentioned, what I said "probably does not > reflect an Emacs doc-string or manual convention". FWIW, I agree with the use of unquoted "cdr" in the present case. Especially since the notion of proper list is one that can't be understood before you understand cons/car/cdr, and that there's AFAIK no possible confusion with a normal English word (e.g. I'd be less confident with the use of "car"). Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-04 12:12 ` Basil L. Contovounesios 2018-06-04 14:08 ` Stefan Monnier @ 2018-06-05 1:23 ` Paul Eggert 2018-06-05 2:57 ` Drew Adams 2018-06-05 15:05 ` Basil L. Contovounesios 1 sibling, 2 replies; 100+ messages in thread From: Paul Eggert @ 2018-06-05 1:23 UTC (permalink / raw) To: Basil L. Contovounesios, emacs-devel On 06/04/2018 05:12 AM, Basil L. Contovounesios wrote: > +(defun list-true-p (object) > + "Return t if OBJECT is a true list. > +A true list is neither circular nor dotted (i.e., its last `cdr' > +is nil)." > + (null (nthcdr (safe-length object) object))) This traverses the list twice. Wouldn't it be better to traverse it just once? Also, why not return the length of the list when it is proper? That would not cost anything to compute, and would yield more information than just returning t. Like others, I prefer "proper" to "true". Something like the following, perhaps: (defun proper-list-length (obj) "Return OBJ's length if OBJ is a proper list, nil otherwise." (and (listp obj) (ignore-errors (length obj)))) ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-06-05 1:23 ` Paul Eggert @ 2018-06-05 2:57 ` Drew Adams 2018-06-05 3:08 ` Drew Adams 2018-06-05 3:25 ` Drew Adams 2018-06-05 15:05 ` Basil L. Contovounesios 1 sibling, 2 replies; 100+ messages in thread From: Drew Adams @ 2018-06-05 2:57 UTC (permalink / raw) To: Paul Eggert, Basil L. Contovounesios, emacs-devel From (elisp) `Cons Cells': Also by convention, the CDR of the last cons cell in a list is ‘nil’. We call such a ‘nil’-terminated structure a “true list”. ... For convenience, the symbol ‘nil’ is considered to have ‘nil’ as its CDR (and also as its CAR). That just says: (null (cdr (last xs))), which is (and list (nthcdr (1- (safe-length xs)) xs))). ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-06-05 2:57 ` Drew Adams @ 2018-06-05 3:08 ` Drew Adams 2018-06-05 3:12 ` Drew Adams 2018-06-05 3:25 ` Drew Adams 1 sibling, 1 reply; 100+ messages in thread From: Drew Adams @ 2018-06-05 3:08 UTC (permalink / raw) To: Paul Eggert, Basil L. Contovounesios, emacs-devel > (null (cdr (last xs))), which is > (and list (nthcdr (1- (safe-length xs)) xs))). Forgot to mention that by the manual's definition of "true list", and by this code for it, a circular list _is_ a true list. Dunno whether that was the intention of the OP's "true list". (null (cdr (last #1='(2 3 #1#)))) = t ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-06-05 3:08 ` Drew Adams @ 2018-06-05 3:12 ` Drew Adams 0 siblings, 0 replies; 100+ messages in thread From: Drew Adams @ 2018-06-05 3:12 UTC (permalink / raw) To: Paul Eggert, Basil L. Contovounesios, emacs-devel > > (null (cdr (last xs))), which is > > (and list (nthcdr (1- (safe-length xs)) xs))). > > Forgot to mention that by the manual's definition of "true list", > and by this code for it, a circular list _is_ a true list. Dunno > whether that was the intention of the OP's "true list". > > (null (cdr (last #1='(2 3 #1#)))) = t I should have said that _some_ circular lists are "true lists". (null (cdr (last #1='(2 3 #1# . 5)))) = nil ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-06-05 2:57 ` Drew Adams 2018-06-05 3:08 ` Drew Adams @ 2018-06-05 3:25 ` Drew Adams 1 sibling, 0 replies; 100+ messages in thread From: Drew Adams @ 2018-06-05 3:25 UTC (permalink / raw) To: Paul Eggert, Basil L. Contovounesios, emacs-devel > That just says: (null (cdr (last xs))), which is > (and xs (nthcdr (1- (safe-length xs)) xs))). Sheesh; sorry. That should have been just what Philip wrote in the beginning: (null (nthcdr (safe-length xs) xs)). ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-05 1:23 ` Paul Eggert 2018-06-05 2:57 ` Drew Adams @ 2018-06-05 15:05 ` Basil L. Contovounesios 2018-06-06 7:42 ` Paul Eggert 1 sibling, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-06-05 15:05 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel Paul Eggert <eggert@cs.ucla.edu> writes: > On 06/04/2018 05:12 AM, Basil L. Contovounesios wrote: >> +(defun list-true-p (object) >> + "Return t if OBJECT is a true list. >> +A true list is neither circular nor dotted (i.e., its last `cdr' >> +is nil)." >> + (null (nthcdr (safe-length object) object))) > > This traverses the list twice. Wouldn't it be better to traverse it just once? Of course. > Also, why not return the length of the list when it is proper? That would not > cost anything to compute, and would yield more information than just returning > t. I prefer your suggestion to mine. Would "(elisp) List Elements" be a more appropriate place to document this function then? > Like others, I prefer "proper" to "true". OK, but my question still stands: do we use the term "proper" only to name this function, but keep existing references to "true" lists in the manual? Or do we systematically switch from "true" to "proper", and possibly add a historical sidenote in the manual documenting this change in preferred terminology? > Something like the following, perhaps: > > (defun proper-list-length (obj) > "Return OBJ's length if OBJ is a proper list, nil otherwise." > (and (listp obj) (ignore-errors (length obj)))) LGTM. Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-05 15:05 ` Basil L. Contovounesios @ 2018-06-06 7:42 ` Paul Eggert 2018-06-06 9:40 ` Van L 2018-07-05 22:31 ` Basil L. Contovounesios 0 siblings, 2 replies; 100+ messages in thread From: Paul Eggert @ 2018-06-06 7:42 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: emacs-devel Basil L. Contovounesios wrote: > OK, but my question still stands: do we use the term "proper" only to > name this function, but keep existing references to "true" lists in the > manual? Or do we systematically switch from "true" to "proper" I suggest the latter. I've often heard them called "proper lists", and the phrase "true list" is less common in my experience. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-06 7:42 ` Paul Eggert @ 2018-06-06 9:40 ` Van L 2018-06-06 13:44 ` Stefan Monnier 2018-07-05 22:31 ` Basil L. Contovounesios 1 sibling, 1 reply; 100+ messages in thread From: Van L @ 2018-06-06 9:40 UTC (permalink / raw) To: Emacs-Devel devel > Paul Eggert writes: > > Basil L. Contovounesios writes: >> OK, but my question still stands: do we use the term "proper" only to >> name this function, but keep existing references to "true" lists in the >> manual? Or do we systematically switch from "true" to "proper" > > I suggest the latter. I've often heard them called "proper lists", and the phrase "true list" is less common in my experience. How about "fit"? short for fitness function. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-06 9:40 ` Van L @ 2018-06-06 13:44 ` Stefan Monnier 2018-06-06 17:40 ` Stefan Monnier 2018-06-07 7:03 ` Van L 0 siblings, 2 replies; 100+ messages in thread From: Stefan Monnier @ 2018-06-06 13:44 UTC (permalink / raw) To: emacs-devel >>> OK, but my question still stands: do we use the term "proper" only to >>> name this function, but keep existing references to "true" lists in the >>> manual? Or do we systematically switch from "true" to "proper" >> I suggest the latter. I've often heard them called "proper lists", and the >> phrase "true list" is less common in my experience. > How about "fit"? short for fitness function. You mean, invent our own names, to be sure everyone is equally confused? A bit like our use of "frames" for "windows", but without the justification of the historical accident? Sounds like a great plan. Then I suggest we name this function `terminated-enumeration-p` Stefan "not" ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-06 13:44 ` Stefan Monnier @ 2018-06-06 17:40 ` Stefan Monnier 2018-06-07 7:03 ` Van L 1 sibling, 0 replies; 100+ messages in thread From: Stefan Monnier @ 2018-06-06 17:40 UTC (permalink / raw) To: emacs-devel > Stefan "not" ^^ or Duh! Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-06 13:44 ` Stefan Monnier 2018-06-06 17:40 ` Stefan Monnier @ 2018-06-07 7:03 ` Van L 1 sibling, 0 replies; 100+ messages in thread From: Van L @ 2018-06-07 7:03 UTC (permalink / raw) To: Emacs-Devel devel > Stefan Monnier writes: > >> How about "fit"? short for fitness function. > > You mean, invent our own names, to be sure everyone is equally confused? > A bit like our use of "frames" for "windows", but without the > justification of the historical accident? > > Sounds like a great plan. > > Then I suggest we name this function `terminated-enumeration-p` I have heard `fit` used as a `suitcase word` to pack the idea for `failure in time` among other things. In that sense, fit would be consistent with the origin of car and cdr. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-06-06 7:42 ` Paul Eggert 2018-06-06 9:40 ` Van L @ 2018-07-05 22:31 ` Basil L. Contovounesios 2018-07-06 5:57 ` Eli Zaretskii ` (2 more replies) 1 sibling, 3 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-05 22:31 UTC (permalink / raw) To: Paul Eggert; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1582 bytes --] Paul Eggert <eggert@cs.ucla.edu> writes: > Basil L. Contovounesios wrote: >> OK, but my question still stands: do we use the term "proper" only to >> name this function, but keep existing references to "true" lists in the >> manual? Or do we systematically switch from "true" to "proper" > > I suggest the latter. I've often heard them called "proper lists", and the > phrase "true list" is less common in my experience. I attach three patches (and a benchmark). The first introduces the function proper-list-length as per your suggestion in https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html (please check whether I have properly attributed you, as I am unfamiliar with the preferred way of doing this). The second changes the Elisp manual to refer to "proper lists" instead of "true lists". The third moves the definition of zerop in lisp/subr.el from under the 'List functions' heading to under the 'Basic Lisp functions' heading. Finally, here are the updated (byte-compiled) benchmark results: ‘proper-list-length’ proper (0.004452047000000001 0 0.0) dotted (0.005584044999999999 0 0.0) circular (0.006193915 0 0.0) ‘format-proper-list-p’ proper (0.06397756299999999 0 0.0) dotted (0.063610087 0 0.0) circular (0.09455345899999999 0 0.0) ‘ert--proper-list-p’ proper (0.29080201899999997 0 0.0) dotted (0.290801063 0 0.0) circular (0.433813842 0 0.0) WDYT? Thanks, -- Basil [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: Add convenience function proper-list-length --] [-- Type: text/x-diff, Size: 11681 bytes --] From f42cb45f449dbb6c3d806398a128bc5914fdebab Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Fri, 6 Jul 2018 00:41:11 +0300 Subject: [PATCH 1/3] Add convenience function proper-list-length * lisp/subr.el (proper-list-length): New function. Suggested by Paul Eggert <eggert@cs.ucla.edu> in https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html. * doc/lispref/lists.texi (List Elements): * etc/NEWS: Mention it. * lisp/emacs-lisp/byte-opt.el (byte-optimize-if): * lisp/emacs-lisp/cl-macs.el (cl--make-usage-args): * lisp/org/ob-core.el (org-babel-insert-result): Use it. * lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove. (ert--explain-equal-rec): Use proper-list-length instead. * lisp/format.el (format-proper-list-p): Remove. (format-annotate-single-property-change): Use proper-list-length instead. * test/lisp/emacs-lisp/ert-tests.el (ert-test-proper-list-p): Move from here... * test/lisp/subr-tests.el (subr-tests--proper-list-length): ...to here, mutatis mutandis. --- doc/lispref/lists.texi | 17 +++++++++++++ etc/NEWS | 6 +++++ lisp/emacs-lisp/byte-opt.el | 2 +- lisp/emacs-lisp/cl-macs.el | 2 +- lisp/emacs-lisp/ert.el | 22 ++++------------ lisp/format.el | 12 ++------- lisp/org/ob-core.el | 5 ++-- lisp/subr.el | 6 +++++ test/lisp/emacs-lisp/ert-tests.el | 42 ------------------------------- test/lisp/subr-tests.el | 18 +++++++++++++ 10 files changed, 58 insertions(+), 74 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 761750eb20..6850e9d74e 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -338,6 +338,23 @@ List Elements If @var{list} is not @code{nil} or a cons cell, @code{safe-length} returns 0. +@end defun + +@defun proper-list-length object +This function returns the length of @var{object} if it is a proper +list, @code{nil} otherwise. In addition to satisfying @code{listp}, a +proper list is neither circular nor dotted. + +@example +@group +(proper-list-length '(a b c)) + @result{} 3 +@end group +@group +(proper-list-length '(a b . c)) + @result{} nil +@end group +@end example @end defun The most common way to compute the length of a list, when you are not diff --git a/etc/NEWS b/etc/NEWS index c92ee6e680..bd1b4509d6 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -689,6 +689,12 @@ manual for more details. \f * Lisp Changes in Emacs 27.1 ++++ +** New function 'proper-list-length'. +Given a proper list as argument, this function returns its length; +otherwise, it returns nil. This function can thus be used as a +predicate for proper lists. + ** define-minor-mode automatically documents the meaning of ARG +++ diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 3bc4c438d6..880bd6982d 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -983,7 +983,7 @@ byte-optimize-if (let ((clause (nth 1 form))) (cond ((and (eq (car-safe clause) 'progn) ;; `clause' is a proper list. - (null (cdr (last clause)))) + (proper-list-length clause)) (if (null (cddr clause)) ;; A trivial `progn'. (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form))) diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index b50961adac..d95448afb8 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -498,7 +498,7 @@ cl--make-usage-args ;; `&aux' args aren't arguments, so let's just drop them from the ;; usage info. (setq arglist (cl-subseq arglist 0 aux)))) - (if (cdr-safe (last arglist)) ;Not a proper list. + (if (not (proper-list-length arglist)) ; Not a proper list. (let* ((last (last arglist)) (tail (cdr last))) (unwind-protect diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index 32bb367cdb..3b50225afb 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el @@ -472,18 +472,6 @@ ert--should-error-handle-error ;; buffer. Perhaps explanations should be reported through `ert-info' ;; rather than as part of the condition. -(defun ert--proper-list-p (x) - "Return non-nil if X is a proper list, nil otherwise." - (cl-loop - for firstp = t then nil - for fast = x then (cddr fast) - for slow = x then (cdr slow) do - (when (null fast) (cl-return t)) - (when (not (consp fast)) (cl-return nil)) - (when (null (cdr fast)) (cl-return t)) - (when (not (consp (cdr fast))) (cl-return nil)) - (when (and (not firstp) (eq fast slow)) (cl-return nil)))) - (defun ert--explain-format-atom (x) "Format the atom X for `ert--explain-equal'." (pcase x @@ -498,12 +486,12 @@ ert--explain-equal-rec `(different-types ,a ,b) (pcase-exhaustive a ((pred consp) - (let ((a-proper-p (ert--proper-list-p a)) - (b-proper-p (ert--proper-list-p b))) - (if (not (eql (not a-proper-p) (not b-proper-p))) + (let ((a-proper-p (proper-list-length a)) + (b-proper-p (proper-list-length b))) + (if (not (eq (not a-proper-p) (not b-proper-p))) `(one-list-proper-one-improper ,a ,b) (if a-proper-p - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(proper-lists-of-different-length ,(length a) ,(length b) ,a ,b first-mismatch-at @@ -523,7 +511,7 @@ ert--explain-equal-rec (cl-assert (equal a b) t) nil)))))))) ((pred arrayp) - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(arrays-of-different-length ,(length a) ,(length b) ,a ,b ,@(unless (char-table-p a) diff --git a/lisp/format.el b/lisp/format.el index 2f198e3eb7..bf535ca174 100644 --- a/lisp/format.el +++ b/lisp/format.el @@ -539,14 +539,6 @@ format-make-relatively-unique (setq tail next))) (cons acopy bcopy))) -(defun format-proper-list-p (list) - "Return t if LIST is a proper list. -A proper list is a list ending with a nil cdr, not with an atom " - (when (listp list) - (while (consp list) - (setq list (cdr list))) - (null list))) - (defun format-reorder (items order) "Arrange ITEMS to follow partial ORDER. Elements of ITEMS equal to elements of ORDER will be rearranged @@ -1005,8 +997,8 @@ format-annotate-single-property-change ;; If either old or new is a list, have to treat both that way. (if (and (or (listp old) (listp new)) (not (get prop 'format-list-atomic-p))) - (if (or (not (format-proper-list-p old)) - (not (format-proper-list-p new))) + (if (not (and (proper-list-length old) + (proper-list-length new))) (format-annotate-atomic-property-change prop-alist old new) (let* ((old (if (listp old) old (list old))) (new (if (listp new) new (list new))) diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index 5d5faaa6fd..09c43d8716 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -2310,10 +2310,9 @@ org-babel-insert-result (lambda (r) ;; Non-nil when result R can be turned into ;; a table. - (and (listp r) - (null (cdr (last r))) + (and (proper-list-length r) (cl-every - (lambda (e) (or (atom e) (null (cdr (last e))))) + (lambda (e) (or (atom e) (proper-list-length e))) result))))) ;; insert results based on type (cond diff --git a/lisp/subr.el b/lisp/subr.el index ca184d8fc8..cb809a4ec7 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -555,6 +555,12 @@ zerop (declare (compiler-macro (lambda (_) `(= 0 ,number)))) (= 0 number)) +(defun proper-list-length (object) + "Return OBJECT's length if it is a proper list, nil otherwise. +A proper list is neither circular nor dotted (i.e., its last cdr +is nil)." + (and (listp object) (ignore-errors (length object)))) + (defun delete-dups (list) "Destructively remove `equal' duplicates from LIST. Store the result in LIST and return it. LIST must be a proper list. diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el index e92b434274..cb957bd9fd 100644 --- a/test/lisp/emacs-lisp/ert-tests.el +++ b/test/lisp/emacs-lisp/ert-tests.el @@ -496,48 +496,6 @@ ert-test--which-file ;;; Tests for utility functions. -(ert-deftest ert-test-proper-list-p () - (should (ert--proper-list-p '())) - (should (ert--proper-list-p '(1))) - (should (ert--proper-list-p '(1 2))) - (should (ert--proper-list-p '(1 2 3))) - (should (ert--proper-list-p '(1 2 3 4))) - (should (not (ert--proper-list-p 'a))) - (should (not (ert--proper-list-p '(1 . a)))) - (should (not (ert--proper-list-p '(1 2 . a)))) - (should (not (ert--proper-list-p '(1 2 3 . a)))) - (should (not (ert--proper-list-p '(1 2 3 4 . a)))) - (let ((a (list 1))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cl-cdddr a)) - (should (not (ert--proper-list-p a))))) - (ert-deftest ert-test-parse-keys-and-body () (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo)))) (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil))) diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 52b61d9fb9..06e9a2b319 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -306,6 +306,24 @@ subr-test--frames-1 (should (eq (string-to-char (symbol-name (gensym))) ?g)) (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) +(ert-deftest subr-tests--proper-list-length () + "Test `proper-list-length' behavior." + (dotimes (length 4) + ;; Proper and dotted lists. + (let ((list (make-list length 0))) + (should (= (proper-list-length list) length)) + (should (not (proper-list-length (nconc list 0))))) + ;; Circular lists. + (dotimes (n (1+ length)) + (let ((circle (make-list (1+ length) 0))) + (should (not (proper-list-length (nconc circle (nthcdr n circle)))))))) + ;; Atoms. + (should (not (proper-list-length 0))) + (should (not (proper-list-length ""))) + (should (not (proper-list-length []))) + (should (not (proper-list-length (make-bool-vector 0 nil)))) + (should (not (proper-list-length (make-symbol "a"))))) + (ert-deftest subr-tests--assq-delete-all () "Test `assq-delete-all' behavior." (cl-flet ((new-list-fn -- 2.18.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #3: Refer to "proper lists" instead of "true lists" --] [-- Type: text/x-diff, Size: 3385 bytes --] From a77c216ff8777843667682721acbf3078f9edeab Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Fri, 6 Jul 2018 00:47:55 +0300 Subject: [PATCH 2/3] Refer to "proper lists" instead of "true lists" * doc/lispref/lists.texi (Cons Cells, Building Lists): * doc/lispref/sequences.texi (Vector Functions): Do it. Suggested in the following emacs-devel messages: https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00112.html https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html --- doc/lispref/lists.texi | 13 +++++++------ doc/lispref/sequences.texi | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 6850e9d74e..cd2ff8d2f7 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -50,16 +50,17 @@ Cons Cells slots have similar properties). Hence, the @sc{cdr} slot of each cons cell in a list refers to the following cons cell. +@cindex proper list @cindex true list Also by convention, the @sc{cdr} of the last cons cell in a list is @code{nil}. We call such a @code{nil}-terminated structure a -@dfn{true list}. In Emacs Lisp, the symbol @code{nil} is both a +@dfn{proper list}. In Emacs Lisp, the symbol @code{nil} is both a symbol and a list with no elements. For convenience, the symbol @code{nil} is considered to have @code{nil} as its @sc{cdr} (and also as its @sc{car}). - Hence, the @sc{cdr} of a true list is always a true list. The -@sc{cdr} of a nonempty true list is a true list containing all the + Hence, the @sc{cdr} of a proper list is always a proper list. The +@sc{cdr} of a nonempty proper list is a proper list containing all the elements except the first. @cindex dotted list @@ -71,10 +72,10 @@ Cons Cells @sc{cdr} could point to one of the previous cons cells in the list. We call that structure a @dfn{circular list}. - For some purposes, it does not matter whether a list is true, + For some purposes, it does not matter whether a list is proper, circular or dotted. If a program doesn't look far enough down the list to see the @sc{cdr} of the final cons cell, it won't care. -However, some functions that operate on lists demand true lists and +However, some functions that operate on lists demand proper lists and signal errors if given a dotted list. Most functions that try to find the end of a list enter infinite loops if given a circular list. @@ -539,7 +540,7 @@ Building Lists is itself a list, then its elements become in effect elements of the result list. If the final element is not a list, the result is a dotted list since its final @sc{cdr} is not @code{nil} as required -in a true list. +in a proper list. @end defun Here is an example of using @code{append}: diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 566ba8de18..8eb877882c 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -1353,7 +1353,7 @@ Vector Functions @defun vconcat &rest sequences @cindex copying vectors This function returns a new vector containing all the elements of -@var{sequences}. The arguments @var{sequences} may be true lists, +@var{sequences}. The arguments @var{sequences} may be proper lists, vectors, strings or bool-vectors. If no @var{sequences} are given, the empty vector is returned. -- 2.18.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #4: Rearrange definition of zerop in subr.el --] [-- Type: text/x-diff, Size: 1424 bytes --] From 772881e0c18f68c7651fabd637948cd5a87e7286 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Fri, 6 Jul 2018 00:56:45 +0300 Subject: [PATCH 3/3] ; Rearrange definition of zerop in subr.el * lisp/subr.el (zerop): Move from under 'List functions' heading to under 'Basic Lisp functions' heading. --- lisp/subr.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lisp/subr.el b/lisp/subr.el index cb809a4ec7..15a5d7b9e4 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -359,6 +359,13 @@ apply-partially (lambda (&rest args2) (apply fun (append args args2)))) +(defun zerop (number) + "Return t if NUMBER is zero." + ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because + ;; = has a byte-code. + (declare (compiler-macro (lambda (_) `(= 0 ,number)))) + (= 0 number)) + \f ;;;; List functions. @@ -548,13 +555,6 @@ nbutlast (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil)) list)))) -(defun zerop (number) - "Return t if NUMBER is zero." - ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because - ;; = has a byte-code. - (declare (compiler-macro (lambda (_) `(= 0 ,number)))) - (= 0 number)) - (defun proper-list-length (object) "Return OBJECT's length if it is a proper list, nil otherwise. A proper list is neither circular nor dotted (i.e., its last cdr -- 2.18.0 [-- Attachment #5: Benchmark for proper-list-length --] [-- Type: application/emacs-lisp, Size: 936 bytes --] ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-05 22:31 ` Basil L. Contovounesios @ 2018-07-06 5:57 ` Eli Zaretskii 2018-07-06 17:16 ` Drew Adams ` (2 more replies) 2018-07-06 17:00 ` Drew Adams 2018-07-06 17:30 ` Paul Eggert 2 siblings, 3 replies; 100+ messages in thread From: Eli Zaretskii @ 2018-07-06 5:57 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: eggert, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Date: Fri, 06 Jul 2018 01:31:26 +0300 > Cc: emacs-devel@gnu.org > > I attach three patches (and a benchmark). The first introduces the > function proper-list-length as per your suggestion in > https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html > (please check whether I have properly attributed you, as I am unfamiliar > with the preferred way of doing this). > > The second changes the Elisp manual to refer to "proper lists" instead > of "true lists". > > The third moves the definition of zerop in lisp/subr.el from under the > 'List functions' heading to under the 'Basic Lisp functions' heading. > > Finally, here are the updated (byte-compiled) benchmark results: > > ‘proper-list-length’ > proper (0.004452047000000001 0 0.0) > dotted (0.005584044999999999 0 0.0) > circular (0.006193915 0 0.0) > ‘format-proper-list-p’ > proper (0.06397756299999999 0 0.0) > dotted (0.063610087 0 0.0) > circular (0.09455345899999999 0 0.0) > ‘ert--proper-list-p’ > proper (0.29080201899999997 0 0.0) > dotted (0.290801063 0 0.0) > circular (0.433813842 0 0.0) > > WDYT? Thanks, I have a few comments below. > >From f42cb45f449dbb6c3d806398a128bc5914fdebab Mon Sep 17 00:00:00 2001 > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Date: Fri, 6 Jul 2018 00:41:11 +0300 > Subject: [PATCH 1/3] Add convenience function proper-list-length > > * lisp/subr.el (proper-list-length): New function. > Suggested by Paul Eggert <eggert@cs.ucla.edu> in > https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html. > * doc/lispref/lists.texi (List Elements): > * etc/NEWS: Mention it. The "it" part here is ambiguous, because it's too far from the last place where the function's name was mentioned. Please state the name of the function for better readability. > * lisp/emacs-lisp/byte-opt.el (byte-optimize-if): > * lisp/emacs-lisp/cl-macs.el (cl--make-usage-args): > * lisp/org/ob-core.el (org-babel-insert-result): Use it. Likewise. > * lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove. > (ert--explain-equal-rec): Use proper-list-length instead. > * lisp/format.el (format-proper-list-p): Remove. Maybe consider mentioning that these are removed "because SUCH-AND-SUCH replaces them in file FOO." > +@defun proper-list-length object > +This function returns the length of @var{object} if it is a proper > +list, @code{nil} otherwise. In addition to satisfying @code{listp}, a > +proper list is neither circular nor dotted. Proper list is defined elsewhere in the manual, so please add here a cross-reference to that spot (I'd suggest doing that with @pxref in parentheses at the end of the first sentence). > +** New function 'proper-list-length'. > +Given a proper list as argument, this function returns its length; > +otherwise, it returns nil. This function can thus be used as a > +predicate for proper lists. Do we really want this usage of the function as a predicate? I find this slightly unnatural, and also not future-proof enough, because you rely on the checks 'length' does internally. If the internals of 'length' change one day, this predicate usage will collapse like a house of cards. Would it make more sense to have a separate predicate? > +(defun proper-list-length (object) > + "Return OBJECT's length if it is a proper list, nil otherwise. > +A proper list is neither circular nor dotted (i.e., its last cdr > +is nil)." But if we do want to use this as a predicate, then the doc string should say so. > * doc/lispref/lists.texi (Cons Cells, Building Lists): > * doc/lispref/sequences.texi (Vector Functions): Do it. Please put the full description in the log entry. "Do it" refers to what the header says, I presume, but I at least am used to ignoring the headers and reading the entries, because they are generally more informative (due to space constraints on the header). > +@cindex proper list > @cindex true list > Also by convention, the @sc{cdr} of the last cons cell in a list is > @code{nil}. We call such a @code{nil}-terminated structure a > -@dfn{true list}. In Emacs Lisp, the symbol @code{nil} is both a > +@dfn{proper list}. In Emacs Lisp, the symbol @code{nil} is both a We still have "true list" in the index (and rightly so), so I think the new text should say, perhaps in parens or in a footnote, that such lists are also known as "true lists". Imagine a reader who follows the "true list" index entry and gets placed on this text -- they will be confused to not see "true list" mentioned anywhere. Besides, that term is in wide usage elsewhere. Thanks again for working on this. ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-07-06 5:57 ` Eli Zaretskii @ 2018-07-06 17:16 ` Drew Adams 2018-07-06 17:38 ` Eli Zaretskii ` (2 more replies) 2018-07-06 18:04 ` Paul Eggert 2018-07-09 19:25 ` Basil L. Contovounesios 2 siblings, 3 replies; 100+ messages in thread From: Drew Adams @ 2018-07-06 17:16 UTC (permalink / raw) To: Eli Zaretskii, Basil L. Contovounesios; +Cc: eggert, emacs-devel > > +** New function 'proper-list-length'. > > +Given a proper list as argument, this function returns its length; > > +otherwise, it returns nil. This function can thus be used as a > > +predicate for proper lists. > > Do we really want this usage of the function as a predicate? I find > this slightly unnatural, and also not future-proof enough, because you > rely on the checks 'length' does internally. If the internals of > 'length' change one day, this predicate usage will collapse like a > house of cards. Would it make more sense to have a separate > predicate? IIRC, the main motivation for adding this function was to provide a predicate for testing properness. I see no reason why we should have two functions that do essentially the same thing: the proposed function to obtain the length plus a function such as this: (defun proper-list-p (object) "Return non-nil if OBJECT is a proper list." (and (proper-list-length object) t)) We should just have one function, and make clear its use as a predicate. If we really want to make that clear and discoverable then provide `proper-list-p' as an alias for `proper-list-length'. That makes both uses clear without having two functions. [The name `proper-list-length' can make it sound like it returns the proper length of a list, not the length of a proper list. But I can't think of a better name. (And `list-proper-length' is what we would really call a function that did provide the "proper length" of a list.)] ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 17:16 ` Drew Adams @ 2018-07-06 17:38 ` Eli Zaretskii [not found] ` <<83601sl0wo.fsf@gnu.org> [not found] ` <<<83601sl0wo.fsf@gnu.org> 2 siblings, 0 replies; 100+ messages in thread From: Eli Zaretskii @ 2018-07-06 17:38 UTC (permalink / raw) To: Drew Adams; +Cc: contovob, eggert, emacs-devel > Date: Fri, 6 Jul 2018 10:16:11 -0700 (PDT) > From: Drew Adams <drew.adams@oracle.com> > Cc: eggert@cs.ucla.edu, emacs-devel@gnu.org > > > Do we really want this usage of the function as a predicate? I find > > this slightly unnatural, and also not future-proof enough, because you > > rely on the checks 'length' does internally. If the internals of > > 'length' change one day, this predicate usage will collapse like a > > house of cards. Would it make more sense to have a separate > > predicate? > > IIRC, the main motivation for adding this function was > to provide a predicate for testing properness. Then I'd prefer calling the function proper-list-p (and returning the list length when it's a proper list) than the other way around. ^ permalink raw reply [flat|nested] 100+ messages in thread
[parent not found: <<83601sl0wo.fsf@gnu.org>]
* RE: Predicate for true lists [not found] ` <<83601sl0wo.fsf@gnu.org> @ 2018-07-06 18:00 ` Drew Adams 2018-07-07 6:54 ` Eli Zaretskii 0 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2018-07-06 18:00 UTC (permalink / raw) To: Eli Zaretskii, Drew Adams; +Cc: contovob, eggert, emacs-devel > > > Do we really want this usage of the function as a predicate? I find > > > this slightly unnatural, and also not future-proof enough, because > > > you rely on the checks 'length' does internally. If the internals of > > > 'length' change one day, this predicate usage will collapse like a > > > house of cards. Would it make more sense to have a separate > > > predicate? > > > > IIRC, the main motivation for adding this function was > > to provide a predicate for testing properness. > > Then I'd prefer calling the function proper-list-p (and returning the > list length when it's a proper list) than the other way around. Then discoverability of the use for length is reduced. Just define it once (one function), using whichever name is preferred, AND alias the other name to the same function. That will help users discover it for both uses. And of course the doc string should point out both uses. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 18:00 ` Drew Adams @ 2018-07-07 6:54 ` Eli Zaretskii 0 siblings, 0 replies; 100+ messages in thread From: Eli Zaretskii @ 2018-07-07 6:54 UTC (permalink / raw) To: Drew Adams; +Cc: contovob, eggert, emacs-devel > Date: Fri, 6 Jul 2018 11:00:43 -0700 (PDT) > From: Drew Adams <drew.adams@oracle.com> > Cc: contovob@tcd.ie, eggert@cs.ucla.edu, emacs-devel@gnu.org > > > > IIRC, the main motivation for adding this function was > > > to provide a predicate for testing properness. > > > > Then I'd prefer calling the function proper-list-p (and returning the > > list length when it's a proper list) than the other way around. > > Then discoverability of the use for length is reduced. Not if we state in the doc string that the value returned for proper lists is the list length. ^ permalink raw reply [flat|nested] 100+ messages in thread
[parent not found: <<<83601sl0wo.fsf@gnu.org>]
[parent not found: <<95fda70b-5893-4788-83c5-a0bb5d708304@default>]
[parent not found: <<8336wvleml.fsf@gnu.org>]
* RE: Predicate for true lists [not found] ` <<8336wvleml.fsf@gnu.org> @ 2018-07-07 14:42 ` Drew Adams 0 siblings, 0 replies; 100+ messages in thread From: Drew Adams @ 2018-07-07 14:42 UTC (permalink / raw) To: Eli Zaretskii, Drew Adams; +Cc: contovob, eggert, emacs-devel > > > > IIRC, the main motivation for adding this function was > > > > to provide a predicate for testing properness. > > > > > > Then I'd prefer calling the function proper-list-p (and returning the > > > list length when it's a proper list) than the other way around. > > > > Then discoverability of the use for length is reduced. > > Not if we state in the doc string that the value returned > for proper lists is the list length. I disagree, but it's not the end of the world if you don't provide the alias. I was referring to _discoverability_ by looking for a function with a name that suggests "length". (And vice versa, if you are looking for a predicate and the only name that exists says "length".) Help functions that work off of the function name will not be very helpful in finding the other function, if only one name is provided. This function has two quite different use cases, and it would help to have two corresponding aliases. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 5:57 ` Eli Zaretskii 2018-07-06 17:16 ` Drew Adams @ 2018-07-06 18:04 ` Paul Eggert 2018-07-07 6:58 ` Eli Zaretskii 2018-07-09 19:25 ` Basil L. Contovounesios 2 siblings, 1 reply; 100+ messages in thread From: Paul Eggert @ 2018-07-06 18:04 UTC (permalink / raw) To: Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 689 bytes --] Eli Zaretskii wrote: > Do we really want this usage of the function as a predicate? I find > this slightly unnatural, Me too, but we haven't come up with a better function semantics, so let's just live with them. I don't care whether the function is called proper-list-length or proper-list-p. (As Drew mentioned, we could have both names.) > and also not future-proof enough, because you > rely on the checks 'length' does internally. I see now that those checks of 'length' were documented incorrectly, so I installed the attached documentation fix into the emacs-26 branch. It should be OK to rely on these checks (after all, they're documented correctly now :-). [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Fix-length-CIRCULAR-documentation.patch --] [-- Type: text/x-patch; name="0001-Fix-length-CIRCULAR-documentation.patch", Size: 1249 bytes --] From 10af9890240d45048cf4553aa731acdb32f7251a Mon Sep 17 00:00:00 2001 From: Paul Eggert <eggert@cs.ucla.edu> Date: Fri, 6 Jul 2018 10:59:53 -0700 Subject: [PATCH] Fix (length CIRCULAR) documentation * doc/lispref/sequences.texi (Sequence Functions): Correct documentation of what (length X) does when X is a circular list. --- doc/lispref/sequences.texi | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 76a4a46..59faf2b 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -75,9 +75,9 @@ Sequence Functions @anchor{Definition of length} This function returns the number of elements in @var{sequence}. If @var{sequence} is a dotted list, a @code{wrong-type-argument} error is -signaled. Circular lists may cause an infinite loop. For a -char-table, the value returned is always one more than the maximum -Emacs character code. +signaled; if it is a circular list, a @code{circular-list} error is +signaled. For a char-table, the value returned is always one more +than the maximum Emacs character code. @xref{Definition of safe-length}, for the related function @code{safe-length}. -- 2.7.4 ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 18:04 ` Paul Eggert @ 2018-07-07 6:58 ` Eli Zaretskii 2018-07-07 7:20 ` martin rudalics 2018-07-07 8:41 ` Paul Eggert 0 siblings, 2 replies; 100+ messages in thread From: Eli Zaretskii @ 2018-07-07 6:58 UTC (permalink / raw) To: Paul Eggert; +Cc: contovob, emacs-devel > Cc: emacs-devel@gnu.org > From: Paul Eggert <eggert@cs.ucla.edu> > Date: Fri, 6 Jul 2018 11:04:55 -0700 > > Eli Zaretskii wrote: > > Do we really want this usage of the function as a predicate? I find > > this slightly unnatural, > > Me too, but we haven't come up with a better function semantics, so let's just > live with them. I don't care whether the function is called proper-list-length > or proper-list-p. (As Drew mentioned, we could have both names.) I'd prefer proper-list-p, as this is going to be by far the most frequent use of the function, as evidenced by the patch. > > and also not future-proof enough, because you > > rely on the checks 'length' does internally. > > I see now that those checks of 'length' were documented incorrectly, so I > installed the attached documentation fix into the emacs-26 branch. It should be > OK to rely on these checks (after all, they're documented correctly now :-). Thanks, but the documentation still doesn't say that 'length' signals an error for anything that is not a sequence, which is what proper-list-p is relying on. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 6:58 ` Eli Zaretskii @ 2018-07-07 7:20 ` martin rudalics 2018-07-07 8:41 ` Paul Eggert 1 sibling, 0 replies; 100+ messages in thread From: martin rudalics @ 2018-07-07 7:20 UTC (permalink / raw) To: Eli Zaretskii, Paul Eggert; +Cc: contovob, emacs-devel > I'd prefer proper-list-p, as this is going to be by far the most > frequent use of the function, as evidenced by the patch. I prefer 'proper-list-p' too. martin ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 6:58 ` Eli Zaretskii 2018-07-07 7:20 ` martin rudalics @ 2018-07-07 8:41 ` Paul Eggert 2018-07-07 10:04 ` Eli Zaretskii 1 sibling, 1 reply; 100+ messages in thread From: Paul Eggert @ 2018-07-07 8:41 UTC (permalink / raw) To: Eli Zaretskii; +Cc: contovob, emacs-devel Eli Zaretskii wrote: > the documentation still doesn't say that 'length' signals > an error for anything that is not a sequence, which is what > proper-list-p is relying on. The proposed implementation of proper-list-p does not rely on such a signal, because it invokes 'length' only on nil and conses, and a cons must be either a proper list, a dotted list, or a circular list. Since the 'length' documentation specifies behavior in all these cases, the proposed implementation of proper-list-p does not need the 'length' documentation to also say that 'length' signals an error for non-sequences. (Perhaps it is a good idea to add such documentation for other reasons, but that's a different matter.) ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 8:41 ` Paul Eggert @ 2018-07-07 10:04 ` Eli Zaretskii 2018-07-07 15:04 ` Basil L. Contovounesios 2018-07-07 17:06 ` Basil L. Contovounesios 0 siblings, 2 replies; 100+ messages in thread From: Eli Zaretskii @ 2018-07-07 10:04 UTC (permalink / raw) To: Paul Eggert; +Cc: contovob, emacs-devel > Cc: contovob@tcd.ie, emacs-devel@gnu.org > From: Paul Eggert <eggert@cs.ucla.edu> > Date: Sat, 7 Jul 2018 01:41:38 -0700 > > Eli Zaretskii wrote: > > the documentation still doesn't say that 'length' signals > > an error for anything that is not a sequence, which is what > > proper-list-p is relying on. > > The proposed implementation of proper-list-p does not rely on such a signal, That's not my reading of the implementation: (and (listp object) (ignore-errors (length object))) ^^^^^^^^^^^^^ ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 10:04 ` Eli Zaretskii @ 2018-07-07 15:04 ` Basil L. Contovounesios 2018-07-07 16:12 ` Eli Zaretskii 2018-07-07 17:06 ` Basil L. Contovounesios 1 sibling, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-07 15:04 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Paul Eggert, emacs-devel [-- Attachment #1: Type: text/plain, Size: 1109 bytes --] Eli Zaretskii <eliz@gnu.org> writes: >> Cc: contovob@tcd.ie, emacs-devel@gnu.org >> From: Paul Eggert <eggert@cs.ucla.edu> >> Date: Sat, 7 Jul 2018 01:41:38 -0700 >> >> Eli Zaretskii wrote: >> > the documentation still doesn't say that 'length' signals >> > an error for anything that is not a sequence, which is what >> > proper-list-p is relying on. >> >> The proposed implementation of proper-list-p does not rely on such a signal, > > That's not my reading of the implementation: > > (and (listp object) (ignore-errors (length object))) > ^^^^^^^^^^^^^ The call to 'length' is wrapped in 'ignore-errors' in order to catch the errors signalled in 'Flength' by 'FOR_EACH_TAIL' (in case of circularity) and 'CHECK_LIST_END' (in case of dottedness). 'length' shouldn't signal a 'wrong-type-argument' for a non-sequence argument because it is only called on objects which satisfy 'listp': (and (listp object) (ignore-errors (length object))) ^^^^^^^^^^^^^^ Having said that, would something like the following documentation change be welcome, for completeness? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Fix-length-NON-SEQUENCE-documentation.patch --] [-- Type: text/x-diff, Size: 1634 bytes --] From 430d858d3617a11a5130a3344a131b5ab976e818 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Sat, 7 Jul 2018 17:52:26 +0300 Subject: [PATCH] Fix (length NON-SEQUENCE) documentation For discussion, see thread starting at https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00171.html. * doc/lispref/sequences.texi (Sequence Functions): Mention that 'length' signals a 'wrong-type-argument' also when given a non-sequencep argument. --- doc/lispref/sequences.texi | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 59faf2b4f1..e16674db04 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -71,13 +71,14 @@ Sequence Functions @cindex list length @cindex vector length @cindex sequence length +@cindex bool-vector length @cindex char-table length @anchor{Definition of length} This function returns the number of elements in @var{sequence}. If -@var{sequence} is a dotted list, a @code{wrong-type-argument} error is -signaled; if it is a circular list, a @code{circular-list} error is -signaled. For a char-table, the value returned is always one more -than the maximum Emacs character code. +@var{sequence} does not satisfy @code{sequencep} or is a dotted list, +a @code{wrong-type-argument} error is signaled; if it is a circular +list, a @code{circular-list} error is signaled. For a char-table, the value +returned is always one more than the maximum Emacs character code. @xref{Definition of safe-length}, for the related function @code{safe-length}. -- 2.18.0 [-- Attachment #3: Type: text/plain, Size: 11 bytes --] -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 15:04 ` Basil L. Contovounesios @ 2018-07-07 16:12 ` Eli Zaretskii 2018-07-07 16:52 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2018-07-07 16:12 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: eggert, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: Paul Eggert <eggert@cs.ucla.edu>, <emacs-devel@gnu.org> > Date: Sat, 07 Jul 2018 18:04:28 +0300 > > > (and (listp object) (ignore-errors (length object))) > > ^^^^^^^^^^^^^ > > The call to 'length' is wrapped in 'ignore-errors' in order to catch the > errors signalled in 'Flength' by 'FOR_EACH_TAIL' (in case of > circularity) and 'CHECK_LIST_END' (in case of dottedness). 'length' > shouldn't signal a 'wrong-type-argument' for a non-sequence argument > because it is only called on objects which satisfy 'listp': > > (and (listp object) (ignore-errors (length object))) > ^^^^^^^^^^^^^^ Yes, I know. > This function returns the number of elements in @var{sequence}. If > -@var{sequence} is a dotted list, a @code{wrong-type-argument} error is > -signaled; if it is a circular list, a @code{circular-list} error is > -signaled. For a char-table, the value returned is always one more > -than the maximum Emacs character code. > +@var{sequence} does not satisfy @code{sequencep} or is a dotted list, It is better to say "If the argument is not a sequence, or is a dotted list, ...". > +a @code{wrong-type-argument} error is signaled; if it is a circular Please try to avoid passive tense, it usually makes the text longer and less clear. I'd suggest The function signals the @code{wrong-type-argument} error if the argument is not a sequence or is a dotted list; it signals the @code{circular-list} error if the argument is a circular list. Thanks. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 16:12 ` Eli Zaretskii @ 2018-07-07 16:52 ` Basil L. Contovounesios 2018-07-07 17:07 ` Eli Zaretskii 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-07 16:52 UTC (permalink / raw) To: Eli Zaretskii; +Cc: eggert, emacs-devel [-- Attachment #1: Type: text/plain, Size: 1123 bytes --] Eli Zaretskii <eliz@gnu.org> writes: >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Cc: Paul Eggert <eggert@cs.ucla.edu>, <emacs-devel@gnu.org> >> Date: Sat, 07 Jul 2018 18:04:28 +0300 >> >> This function returns the number of elements in @var{sequence}. If >> -@var{sequence} is a dotted list, a @code{wrong-type-argument} error is >> -signaled; if it is a circular list, a @code{circular-list} error is >> -signaled. For a char-table, the value returned is always one more >> -than the maximum Emacs character code. >> +@var{sequence} does not satisfy @code{sequencep} or is a dotted list, > > It is better to say "If the argument is not a sequence, or is a dotted > list, ...". > >> +a @code{wrong-type-argument} error is signaled; if it is a circular > > Please try to avoid passive tense, it usually makes the text longer > and less clear. I'd suggest > > The function signals the @code{wrong-type-argument} error if the > argument is not a sequence or is a dotted list; it signals the > @code{circular-list} error if the argument is a circular list. Thanks, I agree. How's the updated patch: [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Fix-length-NON-SEQUENCE-documentation.patch --] [-- Type: text/x-diff, Size: 1807 bytes --] From 41fda89f5a161521dcc303e79f308b7c13a61b9d Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Sat, 7 Jul 2018 19:33:08 +0300 Subject: [PATCH] Fix (length NON-SEQUENCE) documentation Suggested by Eli Zaretskii <eliz@gnu.org> in the following threads: https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00171.html https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00206.html * doc/lispref/sequences.texi (Sequence Functions): Mention that 'length' signals a 'wrong-type-argument' also when given a non-sequencep argument. --- doc/lispref/sequences.texi | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 59faf2b4f1..188a345114 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -71,13 +71,15 @@ Sequence Functions @cindex list length @cindex vector length @cindex sequence length +@cindex bool-vector length @cindex char-table length @anchor{Definition of length} -This function returns the number of elements in @var{sequence}. If -@var{sequence} is a dotted list, a @code{wrong-type-argument} error is -signaled; if it is a circular list, a @code{circular-list} error is -signaled. For a char-table, the value returned is always one more -than the maximum Emacs character code. +This function returns the number of elements in @var{sequence}. The +function signals the @code{wrong-type-argument} error if the argument +is not a sequence or is a dotted list; it signals the +@code{circular-list} error if the argument is a circular list. For a +char-table, the value returned is always one more than the maximum +Emacs character code. @xref{Definition of safe-length}, for the related function @code{safe-length}. -- 2.18.0 [-- Attachment #3: Type: text/plain, Size: 11 bytes --] -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 16:52 ` Basil L. Contovounesios @ 2018-07-07 17:07 ` Eli Zaretskii 2018-07-07 17:14 ` Paul Eggert 0 siblings, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2018-07-07 17:07 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: eggert, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: <eggert@cs.ucla.edu>, <emacs-devel@gnu.org> > Date: Sat, 07 Jul 2018 19:52:26 +0300 > > Thanks, I agree. How's the updated patch: LGTM, pushed. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 17:07 ` Eli Zaretskii @ 2018-07-07 17:14 ` Paul Eggert 2018-07-07 17:34 ` Eli Zaretskii 2018-07-08 0:15 ` Drew Adams 0 siblings, 2 replies; 100+ messages in thread From: Paul Eggert @ 2018-07-07 17:14 UTC (permalink / raw) To: Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel Eli Zaretskii wrote: > LGTM, pushed. I don't see why the change is needed; on the contrary, it is counterproductive. The documentation for "*" doesn't say that "*" signals an error if an argument is not a number or a marker; it says only that its arguments are numbers or markers. This wording means that if we later extend "*" (say, to support multiplication on vectors of numbers), we won't be making an incompatible change. Similarly, the documentation for "length" need not and should not say that it signals an error if its argument is not a sequence; it should merely say that its argument is a sequence. Otherwise, we'll be precluding future changes from being compatible with the existing documentation. If we document that "length" signals an error if its argument is the wrong type, for consistency shouldn't we have similar documentation for "*" and all other functions that signal errors for wrong-type arguments? That sounds like a lot of verbiage to add the manual, and its added length would be a cost that would exceed any benefit. Also, I still don't understand why this documentation change would be helpful for the proposed proper-list-p implementation. As Eli has acknowledged, the proposed implementation does not depend on 'length' signaling an error when given a non-sequence. So why is this documentation change needed even for this particulare case? ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 17:14 ` Paul Eggert @ 2018-07-07 17:34 ` Eli Zaretskii 2018-07-08 0:15 ` Drew Adams 1 sibling, 0 replies; 100+ messages in thread From: Eli Zaretskii @ 2018-07-07 17:34 UTC (permalink / raw) To: Paul Eggert; +Cc: contovob, emacs-devel > Cc: emacs-devel@gnu.org > From: Paul Eggert <eggert@cs.ucla.edu> > Date: Sat, 7 Jul 2018 10:14:15 -0700 > > If we document that "length" signals an error if its argument is the wrong type, > for consistency shouldn't we have similar documentation for "*" and all other > functions that signal errors for wrong-type arguments? No, because there's no need to be consistent in these matters. ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-07-07 17:14 ` Paul Eggert 2018-07-07 17:34 ` Eli Zaretskii @ 2018-07-08 0:15 ` Drew Adams 2018-07-08 4:48 ` Paul Eggert 1 sibling, 1 reply; 100+ messages in thread From: Drew Adams @ 2018-07-08 0:15 UTC (permalink / raw) To: Paul Eggert, Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel > The documentation for "*" doesn't say that "*" signals an error if an > argument is not a number or a marker; it says only that its arguments > are numbers or markers. This wording means that if we later extend "*" > (say, to support multiplication on vectors of numbers), we won't be > making an incompatible change. I'm sorry to butt in here, but I disagree. Such a change _is_ an incompatible change, _regardless_ of what the doc might say before the change. An incompatible change is about behavior, not just doc. If we at some point changed `*' so that it accepted also vectors and performed a dot- or cross-product operation (or something else), then that would be decided taking into account that the behavior (for vector arguments) changes. For vector arguments the change would, yes, be incompatible, regardless of what the doc might have said. Presumably, if such a decision were ever made, it would be a change for the better - but it would nevertheless an incompatible change. The point of view that we won't document what the current error-handling behavior is, in hopes that that will somehow give us more leeway for changing the behavior later, is misguided. Users should be able to count on the intended behavior - the design, not just what on some part of it that we choose to advertise. We should document the intended behavior, including the error behavior. If we later change the behavior, so be it. At that point we'll change the doc to fit the new behavior. We are all users of Emacs. Doc is a window into the behavior. There is no sense tinting it or blocking part of its view. I spoke of "intended behavior" and "design", as distinct from unintended behavior (e.g. bugs) and implementation details. Raising an error for an argument that is a vector is presumably part of the intended design. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-08 0:15 ` Drew Adams @ 2018-07-08 4:48 ` Paul Eggert 2018-07-08 15:15 ` Drew Adams 0 siblings, 1 reply; 100+ messages in thread From: Paul Eggert @ 2018-07-08 4:48 UTC (permalink / raw) To: Drew Adams, Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel Drew Adams wrote: > We should document the intended behavior, including > the error behavior. Documenting every function's behavior completely, in that function's doc string or manual section, would waste not only our time, but also time spent by users reading the documentation. Nobody does that, for any nontrivial GNU program I'm aware of. Common sense should prevail, and "document every bit of behavior" absolutism would cause more trouble than it'd cure. Thank goodness we don't do that, and have never done that. ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-07-08 4:48 ` Paul Eggert @ 2018-07-08 15:15 ` Drew Adams 2018-07-08 16:00 ` Paul Eggert 0 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2018-07-08 15:15 UTC (permalink / raw) To: Paul Eggert, Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel > > We should document the intended behavior, including > > the error behavior. > > Documenting every function's behavior completely, in that function's doc string or manual section, would waste not only our time, but also time spent by users reading the documentation. Nobody does that, for any nontrivial GNU program I'm aware of. Common sense should prevail, and "document every bit of behavior" absolutism would cause more trouble than it'd cure. Thank goodness we don't do that, and have never done that. Strawman. I never said that every function's behavior needs to be documented completely. Nor is the text you quoted, "document every bit of behavior" something I said - it's just an invention on your part. That strawman distracts from your argument that not documenting behavior can protect future changes from being backward incompatible. And it distracts from my argument against that (misguided) argument. First, I specifically distinguished between design (intended behavior) and implementation (actual behavior, with bugs). It's not at all about documenting "every bit of behavior". And yes, this, like all documentation decisions, is always a judgment call, and yes, such judgment calls for common sense. The question here is whether (1) documenting error-handling behavior in this case is useful to users and (2) whether not documenting it somehow insulates future changes from being backward-incompatible. My point was that not mentioning error-handling behavior does _not_ make a future change less backward-incompatible. We should own the current design and, yes, document it for users. And as Eli mentioned, if and when the design changes we can update the documentation accordingly. That's part (2) of the overall question - the only part I actually addressed, as a reply to your ("absolutist"?) statement to the contrary. And that part (2) applies generally: No documentation omission or spin can protect against backward compatibility. It's a mistake to think or pretend that you're not breaking backward compatibility just because the behavior you change was never documented. Compatibility is about behavior, not just its description. As for part (1), which I did not speak to, there is no hard-and-fast rule. Speaking to it now: I think it probably is useful in this case (for `*'). But that's just one opinion, and given good counter-arguments I might change it. I don't feel strongly about it - I don't much care about this particular case. I posted my reply only because of the wide-ranging, misleading argument you made about doc silence protecting future changes - IOW, part (2). Generally speaking, just because makers of non-free software sometimes try to hide aspects of a design from users, that's no reason why free software should do the same. We have nothing to hide. That does not provide an imperative to pedantically "document every bit of behavior" - no connection. What it does is remove any special incentive (commercial, "intellectual property" related, etc.) to self-censor or otherwise not give users info that could help them. That we don't have a need to hide info does not imply that we need to bore or confuse users with a wall of unhelpful details. Whether this or that bit of info actually helps users, and how much, is a judgment call, on a case-by-case basis. There is no imperative to pedantically document everything (about design or implementation) that derives from the argument I made, nor does my own opinion in this or that case produce such an imperative. --- It's probably worth being clear about another aspect of this. It's quite possible to, by _design_, intend that some part of the currently implemented behavior is "undefined". In that case, yes, there is no explicit commitment to users about that particular bit of behavior. My own opinion is that it can often be a good idea in such a case to explicitly call out that that bit is undefined. That serves as an explicit warning, of sorts, that the behavior might well change in the future. (Any behavior might change in the future; this points out that this particular behavior is undefined.) In the case at hand, is the error-handling behavior of arguments part of the design or just something to be considered undefined - implemented but not really intended? I think, from the discussion, that it's part of the design, but perhaps not? But even in the case where something is considered undefined, and even with an explicit warning about it, a backward-incompatible change is just that - it doesn't become less backward-incompatible just because we excluded that part of the behavior from the design. Backward incompatibility is about actual behavior, always. "The design didn't really change" is not a welcome answer to "You broke my code". It's pretty much a cop-out. This is not a legalistic point of view. It's a view centered on users. It's not about whether the software producer can be held liable or is better able to resist a lawsuit. It's about what is most useful for Emacs and its users. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-08 15:15 ` Drew Adams @ 2018-07-08 16:00 ` Paul Eggert 2018-07-08 17:42 ` Drew Adams 0 siblings, 1 reply; 100+ messages in thread From: Paul Eggert @ 2018-07-08 16:00 UTC (permalink / raw) To: Drew Adams, Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel Drew Adams wrote: > I specifically distinguished between design (intended > behavior) and implementation (actual behavior, with bugs). That's a nonstandard distinction. The more normal distinction is that design covers the strategy for implementing what users want or need, and implementation covers the details of what the code actually does. For now, let's simplify the discussion by stipulating that this code is bug-free, as bugs are a red herring here (nobody is asserting that 'length' has a bug). > My point was that not mentioning error-handling behavior > does _not_ make a future change less backward-incompatible. Your point was incorrect. If a function does not explicitly document its behavior when given a value of the wrong type, there is greater leeway for extension in the future. Conversely, if a function's documentation goes out of its way to say explicitly that it signals an error for a wrong type, even though this is not common practice in function documentation, users will not unreasonably conclude that there's something special about this function, and that it is not likely to be extended in the future in the same way that an ordinary function would be extended. > It's a mistake to think or pretend that you're not breaking > backward compatibility just because the behavior you change > was never documented. No, it's not a mistake at all. Relying on undocumented behavior is more likely to lead to future bugs than not relying on undocumented behavior, and users know (or should know) this when they write their code. So, changing undocumented behavior is less likely to break user code than changing documented behavior is. This is all elementary software engineering. We should not base our work on the theory that any code change that alters machine instructions is "breaking compatibility" (the absolutist position), because that is too strict a notion of "compatibility" to be of much practical use. ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-07-08 16:00 ` Paul Eggert @ 2018-07-08 17:42 ` Drew Adams 2018-07-08 17:47 ` Paul Eggert 0 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2018-07-08 17:42 UTC (permalink / raw) To: Paul Eggert, Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel >> My point was that not mentioning error-handling behavior >> does _not_ make a future change less backward-incompatible. > > Your point was incorrect. If a function does not explicitly > document its behavior when given a value of the wrong type, > there is greater leeway for extension in the future. I've already made my argument. By your reasoning, backward compatibility is a legalistic thing that depends on doc, not behavior. By mine, it's about compatible behavior, not just how well the behavior matches the doc. By your reasoning, providing no doc at all means there is no possibility of backward incompatibility! If a faithful design spec is available then backward compatibility can also be said to be about compatible design changes (which is why I said "intended" behavior). But a faithful design spec makes clear (explicitly or implicitly/logically), the parts that are undefined (bottom). When a design change occurs that is not compatible (same behavior models, or a subset), backward incompatibility results. Whether the change is worth that incompatibility is a design decision. And there is a difference between a faithful design spec and doc. See previous reply, about judgments whether this or that behavior aspect should be documented. Some backward incompatibility may be inconsequential for some, or all, users - it depends. There's a difference between the existence of backward incompatibility and whether or how much users might complain (or even notice). For Emacs development, making design choices that change behavior includes deciding when a backward-incompatible change is worth it. A design choice is not based just on what the doc has said (does the doc give us enough "leeway"?). Doc is a user aid. Doc is neither the design nor the behavior that the design represents. Mixing these things up as you've done, and tossing out condescension about "elementary software engineering", doesn't help. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-08 17:42 ` Drew Adams @ 2018-07-08 17:47 ` Paul Eggert 0 siblings, 0 replies; 100+ messages in thread From: Paul Eggert @ 2018-07-08 17:47 UTC (permalink / raw) To: Drew Adams, Eli Zaretskii, Basil L. Contovounesios; +Cc: emacs-devel Drew Adams wrote: > By your reasoning, > backward compatibility is a legalistic thing that > depends on doc, not behavior. I certainly didn't intend any such thing. Still, I'm afraid that we'll have to continue to disagree about what notions of compatibility are more useful, and about what kind of documentation is more useful. The Emacs documentation mostly follows the guidelines I'm suggesting, thank goodness. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-07 10:04 ` Eli Zaretskii 2018-07-07 15:04 ` Basil L. Contovounesios @ 2018-07-07 17:06 ` Basil L. Contovounesios 1 sibling, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-07 17:06 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Paul Eggert, emacs-devel [-- Attachment #1: Type: text/plain, Size: 947 bytes --] Eli Zaretskii <eliz@gnu.org> writes: >> Cc: contovob@tcd.ie, emacs-devel@gnu.org >> From: Paul Eggert <eggert@cs.ucla.edu> >> Date: Sat, 7 Jul 2018 01:41:38 -0700 >> >> Eli Zaretskii wrote: >> > the documentation still doesn't say that 'length' signals >> > an error for anything that is not a sequence, which is what >> > proper-list-p is relying on. >> >> The proposed implementation of proper-list-p does not rely on such a signal, > > That's not my reading of the implementation: > > (and (listp object) (ignore-errors (length object))) > ^^^^^^^^^^^^^ Sorry, I completely overlooked Paul's message[1] that you were replying to. I now understand what you meant by your reading of the implementation. Given that the two of you suggested both the need for the doc fix and its wording, I think you should ignore my patch[2] and push the change yourself. Here is yet another updated patch, should you disagree: [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Fix-length-NON-SEQUENCE-documentation.patch --] [-- Type: text/x-diff, Size: 1913 bytes --] From 79c6e0e80ebdcd0066654d72514dd08891155a77 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Sat, 7 Jul 2018 19:33:08 +0300 Subject: [PATCH] Fix (length NON-SEQUENCE) documentation Suggested by Eli Zaretskii <eliz@gnu.org> and Paul Eggert <eggert@cs.ucla.edu> in the following threads: https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00171.html https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00177.html https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00206.html * doc/lispref/sequences.texi (Sequence Functions): Mention that 'length' signals a 'wrong-type-argument' also when given a non-sequencep argument. --- doc/lispref/sequences.texi | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 59faf2b4f1..188a345114 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -71,13 +71,15 @@ Sequence Functions @cindex list length @cindex vector length @cindex sequence length +@cindex bool-vector length @cindex char-table length @anchor{Definition of length} -This function returns the number of elements in @var{sequence}. If -@var{sequence} is a dotted list, a @code{wrong-type-argument} error is -signaled; if it is a circular list, a @code{circular-list} error is -signaled. For a char-table, the value returned is always one more -than the maximum Emacs character code. +This function returns the number of elements in @var{sequence}. The +function signals the @code{wrong-type-argument} error if the argument +is not a sequence or is a dotted list; it signals the +@code{circular-list} error if the argument is a circular list. For a +char-table, the value returned is always one more than the maximum +Emacs character code. @xref{Definition of safe-length}, for the related function @code{safe-length}. -- 2.18.0 [-- Attachment #3: Type: text/plain, Size: 169 bytes --] [1]: https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00177.html [2]: https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00207.html Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 5:57 ` Eli Zaretskii 2018-07-06 17:16 ` Drew Adams 2018-07-06 18:04 ` Paul Eggert @ 2018-07-09 19:25 ` Basil L. Contovounesios 2018-07-09 19:40 ` Basil L. Contovounesios 2 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-09 19:25 UTC (permalink / raw) To: Eli Zaretskii; +Cc: eggert, emacs-devel [-- Attachment #1: Type: text/plain, Size: 4479 bytes --] Hmm, I thought I had already sent my reply to this, but I don't see it anywhere; I must have dreamt it. Eli Zaretskii <eliz@gnu.org> writes: >> >From f42cb45f449dbb6c3d806398a128bc5914fdebab Mon Sep 17 00:00:00 2001 >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Date: Fri, 6 Jul 2018 00:41:11 +0300 >> Subject: [PATCH 1/3] Add convenience function proper-list-length >> >> * lisp/subr.el (proper-list-length): New function. >> Suggested by Paul Eggert <eggert@cs.ucla.edu> in >> https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html. >> * doc/lispref/lists.texi (List Elements): >> * etc/NEWS: Mention it. > > The "it" part here is ambiguous, because it's too far from the last > place where the function's name was mentioned. Please state the name > of the function for better readability. > >> * lisp/emacs-lisp/byte-opt.el (byte-optimize-if): >> * lisp/emacs-lisp/cl-macs.el (cl--make-usage-args): >> * lisp/org/ob-core.el (org-babel-insert-result): Use it. > > Likewise. > >> * lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove. >> (ert--explain-equal-rec): Use proper-list-length instead. >> * lisp/format.el (format-proper-list-p): Remove. > > Maybe consider mentioning that these are removed "because > SUCH-AND-SUCH replaces them in file FOO." > >> +@defun proper-list-length object >> +This function returns the length of @var{object} if it is a proper >> +list, @code{nil} otherwise. In addition to satisfying @code{listp}, a >> +proper list is neither circular nor dotted. > > Proper list is defined elsewhere in the manual, so please add here a > cross-reference to that spot (I'd suggest doing that with @pxref in > parentheses at the end of the first sentence). Done x4. >> +** New function 'proper-list-length'. >> +Given a proper list as argument, this function returns its length; >> +otherwise, it returns nil. This function can thus be used as a >> +predicate for proper lists. > > Do we really want this usage of the function as a predicate? I find > this slightly unnatural, and also not future-proof enough, because you > rely on the checks 'length' does internally. If the internals of > 'length' change one day, this predicate usage will collapse like a > house of cards. Would it make more sense to have a separate > predicate? My reading of the ensuing subthread is that it is okay to define proper-list-p such that it relies on Flength's now-documented error behaviour to return a proper list's length. The only alternative I can think of (which avoids traversing a list twice) is to write proper-list-p in C by duplicating the relevant parts of Flength's current behaviour. Any preferences? By the way, is proper-list-p preferred over list-proper-p? >> +(defun proper-list-length (object) >> + "Return OBJECT's length if it is a proper list, nil otherwise. >> +A proper list is neither circular nor dotted (i.e., its last cdr >> +is nil)." > > But if we do want to use this as a predicate, then the doc string > should say so. This isn't necessary if the function is called proper-list-p and is documented under '(elisp) List-related Predicates', right? >> * doc/lispref/lists.texi (Cons Cells, Building Lists): >> * doc/lispref/sequences.texi (Vector Functions): Do it. > > Please put the full description in the log entry. "Do it" refers to > what the header says, I presume, but I at least am used to ignoring > the headers and reading the entries, because they are generally more > informative (due to space constraints on the header). Done. >> +@cindex proper list >> @cindex true list >> Also by convention, the @sc{cdr} of the last cons cell in a list is >> @code{nil}. We call such a @code{nil}-terminated structure a >> -@dfn{true list}. In Emacs Lisp, the symbol @code{nil} is both a >> +@dfn{proper list}. In Emacs Lisp, the symbol @code{nil} is both a > > We still have "true list" in the index (and rightly so), so I think > the new text should say, perhaps in parens or in a footnote, that such > lists are also known as "true lists". Imagine a reader who follows > the "true list" index entry and gets placed on this text -- they will > be confused to not see "true list" mentioned anywhere. Besides, that > term is in wide usage elsewhere. Done as a footnote in the style of the first one under '(elisp) Intro Eval'. I reattach the three patches, updated also for Paul's feedback in https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00149.html. Thanks! -- Basil [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Add-predicate-proper-list-p.patch --] [-- Type: text/x-diff, Size: 12067 bytes --] From d530bcf1d59a01d37b22fea6a832f0578198350d Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Mon, 9 Jul 2018 18:36:43 +0300 Subject: [PATCH 1/3] Add predicate proper-list-p For discussion, see emacs-devel thread starting at https://lists.gnu.org/archive/html/emacs-devel/2018-04/msg00460.html. * lisp/subr.el (proper-list-p): New function. Implementation suggested by Paul Eggert <eggert@cs.ucla.edu> in https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html. * doc/lispref/lists.texi (List Elements): * etc/NEWS: Document proper-list-p. * lisp/org/ob-core.el (org-babel-insert-result): * lisp/emacs-lisp/byte-opt.el (byte-optimize-if): * lisp/emacs-lisp/cl-macs.el (cl--make-usage-args): Use proper-list-p. * lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove. Replaced by proper-list-p in lisp/subr.el. (ert--explain-equal-rec): Use proper-list-length. * lisp/format.el (format-proper-list-p): Remove. Replaced by proper-list-p in lisp/subr.el. (format-annotate-single-property-change): Use proper-list-p. * test/lisp/emacs-lisp/ert-tests.el (ert-test-proper-list-p): Move from here... * test/lisp/subr-tests.el (subr-tests--proper-list-length): ...to here, mutatis mutandis. --- doc/lispref/lists.texi | 16 ++++++++++++ etc/NEWS | 5 ++++ lisp/emacs-lisp/byte-opt.el | 2 +- lisp/emacs-lisp/cl-macs.el | 2 +- lisp/emacs-lisp/ert.el | 28 ++++++--------------- lisp/format.el | 12 ++------- lisp/org/ob-core.el | 5 ++-- lisp/subr.el | 6 +++++ test/lisp/emacs-lisp/ert-tests.el | 42 ------------------------------- test/lisp/subr-tests.el | 18 +++++++++++++ 10 files changed, 59 insertions(+), 77 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 761750eb20..57cefeac96 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -153,6 +153,22 @@ List-related Predicates @end example @end defun +@defun proper-list-p object +This function returns the length of @var{object} if it is a proper +list, @code{nil} otherwise (@pxref{Cons Cells}). In addition to +satisfying @code{listp}, a proper list is neither circular nor dotted. + +@example +@group +(proper-list-p '(a b c)) + @result{} 3 +@end group +@group +(proper-list-p '(a b . c)) + @result{} nil +@end group +@end example +@end defun @node List Elements @section Accessing Elements of Lists diff --git a/etc/NEWS b/etc/NEWS index dae028be7b..1a1e0d8b70 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -703,6 +703,11 @@ manual for more details. \f * Lisp Changes in Emacs 27.1 ++++ +** New function 'proper-list-p'. +Given a proper list as argument, this predicate returns its length; +otherwise, it returns nil. + ** define-minor-mode automatically documents the meaning of ARG +++ diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 3bc4c438d6..11161647a6 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -983,7 +983,7 @@ byte-optimize-if (let ((clause (nth 1 form))) (cond ((and (eq (car-safe clause) 'progn) ;; `clause' is a proper list. - (null (cdr (last clause)))) + (proper-list-p clause)) (if (null (cddr clause)) ;; A trivial `progn'. (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form))) diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index b50961adac..58eadbade0 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -498,7 +498,7 @@ cl--make-usage-args ;; `&aux' args aren't arguments, so let's just drop them from the ;; usage info. (setq arglist (cl-subseq arglist 0 aux)))) - (if (cdr-safe (last arglist)) ;Not a proper list. + (if (not (proper-list-p arglist)) ; Not a proper list. (let* ((last (last arglist)) (tail (cdr last))) (unwind-protect diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index 32bb367cdb..cad21044f1 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el @@ -472,18 +472,6 @@ ert--should-error-handle-error ;; buffer. Perhaps explanations should be reported through `ert-info' ;; rather than as part of the condition. -(defun ert--proper-list-p (x) - "Return non-nil if X is a proper list, nil otherwise." - (cl-loop - for firstp = t then nil - for fast = x then (cddr fast) - for slow = x then (cdr slow) do - (when (null fast) (cl-return t)) - (when (not (consp fast)) (cl-return nil)) - (when (null (cdr fast)) (cl-return t)) - (when (not (consp (cdr fast))) (cl-return nil)) - (when (and (not firstp) (eq fast slow)) (cl-return nil)))) - (defun ert--explain-format-atom (x) "Format the atom X for `ert--explain-equal'." (pcase x @@ -494,17 +482,17 @@ ert--explain-format-atom (defun ert--explain-equal-rec (a b) "Return a programmer-readable explanation of why A and B are not `equal'. Returns nil if they are." - (if (not (equal (type-of a) (type-of b))) + (if (not (eq (type-of a) (type-of b))) `(different-types ,a ,b) (pcase-exhaustive a ((pred consp) - (let ((a-proper-p (ert--proper-list-p a)) - (b-proper-p (ert--proper-list-p b))) - (if (not (eql (not a-proper-p) (not b-proper-p))) + (let ((a-length (proper-list-p a)) + (b-length (proper-list-p b))) + (if (not (eq (not a-length) (not b-length))) `(one-list-proper-one-improper ,a ,b) - (if a-proper-p - (if (not (equal (length a) (length b))) - `(proper-lists-of-different-length ,(length a) ,(length b) + (if a-length + (if (/= a-length b-length) + `(proper-lists-of-different-length ,a-length ,b-length ,a ,b first-mismatch-at ,(cl-mismatch a b :test 'equal)) @@ -523,7 +511,7 @@ ert--explain-equal-rec (cl-assert (equal a b) t) nil)))))))) ((pred arrayp) - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(arrays-of-different-length ,(length a) ,(length b) ,a ,b ,@(unless (char-table-p a) diff --git a/lisp/format.el b/lisp/format.el index 2f198e3eb7..1222abbf65 100644 --- a/lisp/format.el +++ b/lisp/format.el @@ -539,14 +539,6 @@ format-make-relatively-unique (setq tail next))) (cons acopy bcopy))) -(defun format-proper-list-p (list) - "Return t if LIST is a proper list. -A proper list is a list ending with a nil cdr, not with an atom " - (when (listp list) - (while (consp list) - (setq list (cdr list))) - (null list))) - (defun format-reorder (items order) "Arrange ITEMS to follow partial ORDER. Elements of ITEMS equal to elements of ORDER will be rearranged @@ -1005,8 +997,8 @@ format-annotate-single-property-change ;; If either old or new is a list, have to treat both that way. (if (and (or (listp old) (listp new)) (not (get prop 'format-list-atomic-p))) - (if (or (not (format-proper-list-p old)) - (not (format-proper-list-p new))) + (if (not (and (proper-list-p old) + (proper-list-p new))) (format-annotate-atomic-property-change prop-alist old new) (let* ((old (if (listp old) old (list old))) (new (if (listp new) new (list new))) diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index 5d5faaa6fd..a5449fe35e 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -2310,10 +2310,9 @@ org-babel-insert-result (lambda (r) ;; Non-nil when result R can be turned into ;; a table. - (and (listp r) - (null (cdr (last r))) + (and (proper-list-p r) (cl-every - (lambda (e) (or (atom e) (null (cdr (last e))))) + (lambda (e) (or (atom e) (proper-list-p e))) result))))) ;; insert results based on type (cond diff --git a/lisp/subr.el b/lisp/subr.el index ca184d8fc8..c1d90e3fb1 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -555,6 +555,12 @@ zerop (declare (compiler-macro (lambda (_) `(= 0 ,number)))) (= 0 number)) +(defun proper-list-p (object) + "Return OBJECT's length if it is a proper list, nil otherwise. +A proper list is neither circular nor dotted (i.e., its last cdr +is nil)." + (and (listp object) (ignore-errors (length object)))) + (defun delete-dups (list) "Destructively remove `equal' duplicates from LIST. Store the result in LIST and return it. LIST must be a proper list. diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el index e92b434274..cb957bd9fd 100644 --- a/test/lisp/emacs-lisp/ert-tests.el +++ b/test/lisp/emacs-lisp/ert-tests.el @@ -496,48 +496,6 @@ ert-test--which-file ;;; Tests for utility functions. -(ert-deftest ert-test-proper-list-p () - (should (ert--proper-list-p '())) - (should (ert--proper-list-p '(1))) - (should (ert--proper-list-p '(1 2))) - (should (ert--proper-list-p '(1 2 3))) - (should (ert--proper-list-p '(1 2 3 4))) - (should (not (ert--proper-list-p 'a))) - (should (not (ert--proper-list-p '(1 . a)))) - (should (not (ert--proper-list-p '(1 2 . a)))) - (should (not (ert--proper-list-p '(1 2 3 . a)))) - (should (not (ert--proper-list-p '(1 2 3 4 . a)))) - (let ((a (list 1))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cl-cdddr a)) - (should (not (ert--proper-list-p a))))) - (ert-deftest ert-test-parse-keys-and-body () (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo)))) (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil))) diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 52b61d9fb9..86938d5dbe 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -306,6 +306,24 @@ subr-test--frames-1 (should (eq (string-to-char (symbol-name (gensym))) ?g)) (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) +(ert-deftest subr-tests--proper-list-p () + "Test `proper-list-p' behavior." + (dotimes (length 4) + ;; Proper and dotted lists. + (let ((list (make-list length 0))) + (should (= (proper-list-p list) length)) + (should (not (proper-list-p (nconc list 0))))) + ;; Circular lists. + (dotimes (n (1+ length)) + (let ((circle (make-list (1+ length) 0))) + (should (not (proper-list-p (nconc circle (nthcdr n circle)))))))) + ;; Atoms. + (should (not (proper-list-p 0))) + (should (not (proper-list-p ""))) + (should (not (proper-list-p []))) + (should (not (proper-list-p (make-bool-vector 0 nil)))) + (should (not (proper-list-p (make-symbol "a"))))) + (ert-deftest subr-tests--assq-delete-all () "Test `assq-delete-all' behavior." (cl-flet ((new-list-fn -- 2.18.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #3: 0002-Refer-to-proper-lists-instead-of-true-lists.patch --] [-- Type: text/x-diff, Size: 3791 bytes --] From a389dc15aba437db38c06d2782e9ad60f3a5496f Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Mon, 9 Jul 2018 16:25:12 +0300 Subject: [PATCH 2/3] Refer to "proper lists" instead of "true lists" * doc/lispref/lists.texi (Cons Cells, Building Lists): * doc/lispref/sequences.texi (Vector Functions): Use the more popular term "proper", rather than "true", to qualify nil-terminated lists. For discussion, see the following emacs-devel subthreads: https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00112.html https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html --- doc/lispref/lists.texi | 21 ++++++++++++--------- doc/lispref/sequences.texi | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 57cefeac96..b7bb3cf6be 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -50,16 +50,19 @@ Cons Cells slots have similar properties). Hence, the @sc{cdr} slot of each cons cell in a list refers to the following cons cell. +@cindex proper list @cindex true list Also by convention, the @sc{cdr} of the last cons cell in a list is @code{nil}. We call such a @code{nil}-terminated structure a -@dfn{true list}. In Emacs Lisp, the symbol @code{nil} is both a -symbol and a list with no elements. For convenience, the symbol -@code{nil} is considered to have @code{nil} as its @sc{cdr} (and also -as its @sc{car}). +@dfn{proper list}@footnote{It is sometimes also referred to as a +@dfn{true list}, but we generally do not use this terminology in this +manual.}. In Emacs Lisp, the symbol @code{nil} is both a symbol and a +list with no elements. For convenience, the symbol @code{nil} is +considered to have @code{nil} as its @sc{cdr} (and also as its +@sc{car}). - Hence, the @sc{cdr} of a true list is always a true list. The -@sc{cdr} of a nonempty true list is a true list containing all the + Hence, the @sc{cdr} of a proper list is always a proper list. The +@sc{cdr} of a nonempty proper list is a proper list containing all the elements except the first. @cindex dotted list @@ -71,10 +74,10 @@ Cons Cells @sc{cdr} could point to one of the previous cons cells in the list. We call that structure a @dfn{circular list}. - For some purposes, it does not matter whether a list is true, + For some purposes, it does not matter whether a list is proper, circular or dotted. If a program doesn't look far enough down the list to see the @sc{cdr} of the final cons cell, it won't care. -However, some functions that operate on lists demand true lists and +However, some functions that operate on lists demand proper lists and signal errors if given a dotted list. Most functions that try to find the end of a list enter infinite loops if given a circular list. @@ -538,7 +541,7 @@ Building Lists is itself a list, then its elements become in effect elements of the result list. If the final element is not a list, the result is a dotted list since its final @sc{cdr} is not @code{nil} as required -in a true list. +in a proper list (@pxref{Cons Cells}). @end defun Here is an example of using @code{append}: diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 777b1cbbff..62d3305fcf 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -1353,7 +1353,7 @@ Vector Functions @defun vconcat &rest sequences @cindex copying vectors This function returns a new vector containing all the elements of -@var{sequences}. The arguments @var{sequences} may be true lists, +@var{sequences}. The arguments @var{sequences} may be proper lists, vectors, strings or bool-vectors. If no @var{sequences} are given, the empty vector is returned. -- 2.18.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #4: 0003-Rearrange-definition-of-zerop-in-subr.el.patch --] [-- Type: text/x-diff, Size: 1419 bytes --] From a31c3a11b6661381a6a67ebd2f75043b7f3a9305 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Fri, 6 Jul 2018 00:56:45 +0300 Subject: [PATCH 3/3] ; Rearrange definition of zerop in subr.el * lisp/subr.el (zerop): Move from under 'List functions' heading to under 'Basic Lisp functions' heading. --- lisp/subr.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lisp/subr.el b/lisp/subr.el index c1d90e3fb1..10343e69db 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -359,6 +359,13 @@ apply-partially (lambda (&rest args2) (apply fun (append args args2)))) +(defun zerop (number) + "Return t if NUMBER is zero." + ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because + ;; = has a byte-code. + (declare (compiler-macro (lambda (_) `(= 0 ,number)))) + (= 0 number)) + \f ;;;; List functions. @@ -548,13 +555,6 @@ nbutlast (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil)) list)))) -(defun zerop (number) - "Return t if NUMBER is zero." - ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because - ;; = has a byte-code. - (declare (compiler-macro (lambda (_) `(= 0 ,number)))) - (= 0 number)) - (defun proper-list-p (object) "Return OBJECT's length if it is a proper list, nil otherwise. A proper list is neither circular nor dotted (i.e., its last cdr -- 2.18.0 ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-09 19:25 ` Basil L. Contovounesios @ 2018-07-09 19:40 ` Basil L. Contovounesios 2018-07-10 2:02 ` Paul Eggert 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-09 19:40 UTC (permalink / raw) To: Eli Zaretskii; +Cc: eggert, emacs-devel [-- Attachment #1: Type: text/plain, Size: 323 bytes --] "Basil L. Contovounesios" <contovob@tcd.ie> writes: > I reattach the three patches, updated also for Paul's feedback in > https://lists.gnu.org/archive/html/emacs-devel/2018-07/msg00149.html. Sorry, I forgot to remove some now-redundant source comments in my last patchset. Here are the three patches again. -- Basil [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Add-predicate-proper-list-p.patch --] [-- Type: text/x-diff, Size: 12095 bytes --] From 262766620df76ae19ddfe17cb03f03389ee63ada Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Mon, 9 Jul 2018 22:36:25 +0300 Subject: [PATCH 1/3] Add predicate proper-list-p For discussion, see emacs-devel thread starting at https://lists.gnu.org/archive/html/emacs-devel/2018-04/msg00460.html. * lisp/subr.el (proper-list-p): New function. Implementation suggested by Paul Eggert <eggert@cs.ucla.edu> in https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html. * doc/lispref/lists.texi (List Elements): * etc/NEWS: Document proper-list-p. * lisp/org/ob-core.el (org-babel-insert-result): * lisp/emacs-lisp/byte-opt.el (byte-optimize-if): * lisp/emacs-lisp/cl-macs.el (cl--make-usage-args): Use proper-list-p. * lisp/emacs-lisp/ert.el (ert--proper-list-p): Remove. Replaced by proper-list-p in lisp/subr.el. (ert--explain-equal-rec): Use proper-list-length. * lisp/format.el (format-proper-list-p): Remove. Replaced by proper-list-p in lisp/subr.el. (format-annotate-single-property-change): Use proper-list-p. * test/lisp/emacs-lisp/ert-tests.el (ert-test-proper-list-p): Move from here... * test/lisp/subr-tests.el (subr-tests--proper-list-length): ...to here, mutatis mutandis. --- doc/lispref/lists.texi | 16 ++++++++++++ etc/NEWS | 5 ++++ lisp/emacs-lisp/byte-opt.el | 3 +-- lisp/emacs-lisp/cl-macs.el | 2 +- lisp/emacs-lisp/ert.el | 28 ++++++--------------- lisp/format.el | 12 ++------- lisp/org/ob-core.el | 5 ++-- lisp/subr.el | 6 +++++ test/lisp/emacs-lisp/ert-tests.el | 42 ------------------------------- test/lisp/subr-tests.el | 18 +++++++++++++ 10 files changed, 59 insertions(+), 78 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 761750eb20..57cefeac96 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -153,6 +153,22 @@ List-related Predicates @end example @end defun +@defun proper-list-p object +This function returns the length of @var{object} if it is a proper +list, @code{nil} otherwise (@pxref{Cons Cells}). In addition to +satisfying @code{listp}, a proper list is neither circular nor dotted. + +@example +@group +(proper-list-p '(a b c)) + @result{} 3 +@end group +@group +(proper-list-p '(a b . c)) + @result{} nil +@end group +@end example +@end defun @node List Elements @section Accessing Elements of Lists diff --git a/etc/NEWS b/etc/NEWS index dae028be7b..1a1e0d8b70 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -703,6 +703,11 @@ manual for more details. \f * Lisp Changes in Emacs 27.1 ++++ +** New function 'proper-list-p'. +Given a proper list as argument, this predicate returns its length; +otherwise, it returns nil. + ** define-minor-mode automatically documents the meaning of ARG +++ diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 3bc4c438d6..5c0b5e340b 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -982,8 +982,7 @@ byte-optimize-if ;; (if <test> <then> nil) ==> (if <test> <then>) (let ((clause (nth 1 form))) (cond ((and (eq (car-safe clause) 'progn) - ;; `clause' is a proper list. - (null (cdr (last clause)))) + (proper-list-p clause)) (if (null (cddr clause)) ;; A trivial `progn'. (byte-optimize-if `(if ,(cadr clause) ,@(nthcdr 2 form))) diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index b50961adac..011965acb5 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -498,7 +498,7 @@ cl--make-usage-args ;; `&aux' args aren't arguments, so let's just drop them from the ;; usage info. (setq arglist (cl-subseq arglist 0 aux)))) - (if (cdr-safe (last arglist)) ;Not a proper list. + (if (not (proper-list-p arglist)) (let* ((last (last arglist)) (tail (cdr last))) (unwind-protect diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el index 32bb367cdb..cad21044f1 100644 --- a/lisp/emacs-lisp/ert.el +++ b/lisp/emacs-lisp/ert.el @@ -472,18 +472,6 @@ ert--should-error-handle-error ;; buffer. Perhaps explanations should be reported through `ert-info' ;; rather than as part of the condition. -(defun ert--proper-list-p (x) - "Return non-nil if X is a proper list, nil otherwise." - (cl-loop - for firstp = t then nil - for fast = x then (cddr fast) - for slow = x then (cdr slow) do - (when (null fast) (cl-return t)) - (when (not (consp fast)) (cl-return nil)) - (when (null (cdr fast)) (cl-return t)) - (when (not (consp (cdr fast))) (cl-return nil)) - (when (and (not firstp) (eq fast slow)) (cl-return nil)))) - (defun ert--explain-format-atom (x) "Format the atom X for `ert--explain-equal'." (pcase x @@ -494,17 +482,17 @@ ert--explain-format-atom (defun ert--explain-equal-rec (a b) "Return a programmer-readable explanation of why A and B are not `equal'. Returns nil if they are." - (if (not (equal (type-of a) (type-of b))) + (if (not (eq (type-of a) (type-of b))) `(different-types ,a ,b) (pcase-exhaustive a ((pred consp) - (let ((a-proper-p (ert--proper-list-p a)) - (b-proper-p (ert--proper-list-p b))) - (if (not (eql (not a-proper-p) (not b-proper-p))) + (let ((a-length (proper-list-p a)) + (b-length (proper-list-p b))) + (if (not (eq (not a-length) (not b-length))) `(one-list-proper-one-improper ,a ,b) - (if a-proper-p - (if (not (equal (length a) (length b))) - `(proper-lists-of-different-length ,(length a) ,(length b) + (if a-length + (if (/= a-length b-length) + `(proper-lists-of-different-length ,a-length ,b-length ,a ,b first-mismatch-at ,(cl-mismatch a b :test 'equal)) @@ -523,7 +511,7 @@ ert--explain-equal-rec (cl-assert (equal a b) t) nil)))))))) ((pred arrayp) - (if (not (equal (length a) (length b))) + (if (/= (length a) (length b)) `(arrays-of-different-length ,(length a) ,(length b) ,a ,b ,@(unless (char-table-p a) diff --git a/lisp/format.el b/lisp/format.el index 2f198e3eb7..1222abbf65 100644 --- a/lisp/format.el +++ b/lisp/format.el @@ -539,14 +539,6 @@ format-make-relatively-unique (setq tail next))) (cons acopy bcopy))) -(defun format-proper-list-p (list) - "Return t if LIST is a proper list. -A proper list is a list ending with a nil cdr, not with an atom " - (when (listp list) - (while (consp list) - (setq list (cdr list))) - (null list))) - (defun format-reorder (items order) "Arrange ITEMS to follow partial ORDER. Elements of ITEMS equal to elements of ORDER will be rearranged @@ -1005,8 +997,8 @@ format-annotate-single-property-change ;; If either old or new is a list, have to treat both that way. (if (and (or (listp old) (listp new)) (not (get prop 'format-list-atomic-p))) - (if (or (not (format-proper-list-p old)) - (not (format-proper-list-p new))) + (if (not (and (proper-list-p old) + (proper-list-p new))) (format-annotate-atomic-property-change prop-alist old new) (let* ((old (if (listp old) old (list old))) (new (if (listp new) new (list new))) diff --git a/lisp/org/ob-core.el b/lisp/org/ob-core.el index 5d5faaa6fd..a5449fe35e 100644 --- a/lisp/org/ob-core.el +++ b/lisp/org/ob-core.el @@ -2310,10 +2310,9 @@ org-babel-insert-result (lambda (r) ;; Non-nil when result R can be turned into ;; a table. - (and (listp r) - (null (cdr (last r))) + (and (proper-list-p r) (cl-every - (lambda (e) (or (atom e) (null (cdr (last e))))) + (lambda (e) (or (atom e) (proper-list-p e))) result))))) ;; insert results based on type (cond diff --git a/lisp/subr.el b/lisp/subr.el index ca184d8fc8..c1d90e3fb1 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -555,6 +555,12 @@ zerop (declare (compiler-macro (lambda (_) `(= 0 ,number)))) (= 0 number)) +(defun proper-list-p (object) + "Return OBJECT's length if it is a proper list, nil otherwise. +A proper list is neither circular nor dotted (i.e., its last cdr +is nil)." + (and (listp object) (ignore-errors (length object)))) + (defun delete-dups (list) "Destructively remove `equal' duplicates from LIST. Store the result in LIST and return it. LIST must be a proper list. diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el index e92b434274..cb957bd9fd 100644 --- a/test/lisp/emacs-lisp/ert-tests.el +++ b/test/lisp/emacs-lisp/ert-tests.el @@ -496,48 +496,6 @@ ert-test--which-file ;;; Tests for utility functions. -(ert-deftest ert-test-proper-list-p () - (should (ert--proper-list-p '())) - (should (ert--proper-list-p '(1))) - (should (ert--proper-list-p '(1 2))) - (should (ert--proper-list-p '(1 2 3))) - (should (ert--proper-list-p '(1 2 3 4))) - (should (not (ert--proper-list-p 'a))) - (should (not (ert--proper-list-p '(1 . a)))) - (should (not (ert--proper-list-p '(1 2 . a)))) - (should (not (ert--proper-list-p '(1 2 3 . a)))) - (should (not (ert--proper-list-p '(1 2 3 4 . a)))) - (let ((a (list 1))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) a) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cdr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cddr a)) - (should (not (ert--proper-list-p a)))) - (let ((a (list 1 2 3 4))) - (setf (cdr (last a)) (cl-cdddr a)) - (should (not (ert--proper-list-p a))))) - (ert-deftest ert-test-parse-keys-and-body () (should (equal (ert--parse-keys-and-body '(foo)) '(nil (foo)))) (should (equal (ert--parse-keys-and-body '(:bar foo)) '((:bar foo) nil))) diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 52b61d9fb9..86938d5dbe 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -306,6 +306,24 @@ subr-test--frames-1 (should (eq (string-to-char (symbol-name (gensym))) ?g)) (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) +(ert-deftest subr-tests--proper-list-p () + "Test `proper-list-p' behavior." + (dotimes (length 4) + ;; Proper and dotted lists. + (let ((list (make-list length 0))) + (should (= (proper-list-p list) length)) + (should (not (proper-list-p (nconc list 0))))) + ;; Circular lists. + (dotimes (n (1+ length)) + (let ((circle (make-list (1+ length) 0))) + (should (not (proper-list-p (nconc circle (nthcdr n circle)))))))) + ;; Atoms. + (should (not (proper-list-p 0))) + (should (not (proper-list-p ""))) + (should (not (proper-list-p []))) + (should (not (proper-list-p (make-bool-vector 0 nil)))) + (should (not (proper-list-p (make-symbol "a"))))) + (ert-deftest subr-tests--assq-delete-all () "Test `assq-delete-all' behavior." (cl-flet ((new-list-fn -- 2.18.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #3: 0002-Refer-to-proper-lists-instead-of-true-lists.patch --] [-- Type: text/x-diff, Size: 3791 bytes --] From cdeb4497a8539b9b2ab4542554af834d3d1a97a5 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Mon, 9 Jul 2018 16:25:12 +0300 Subject: [PATCH 2/3] Refer to "proper lists" instead of "true lists" * doc/lispref/lists.texi (Cons Cells, Building Lists): * doc/lispref/sequences.texi (Vector Functions): Use the more popular term "proper", rather than "true", to qualify nil-terminated lists. For discussion, see the following emacs-devel subthreads: https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00112.html https://lists.gnu.org/archive/html/emacs-devel/2018-06/msg00138.html --- doc/lispref/lists.texi | 21 ++++++++++++--------- doc/lispref/sequences.texi | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi index 57cefeac96..b7bb3cf6be 100644 --- a/doc/lispref/lists.texi +++ b/doc/lispref/lists.texi @@ -50,16 +50,19 @@ Cons Cells slots have similar properties). Hence, the @sc{cdr} slot of each cons cell in a list refers to the following cons cell. +@cindex proper list @cindex true list Also by convention, the @sc{cdr} of the last cons cell in a list is @code{nil}. We call such a @code{nil}-terminated structure a -@dfn{true list}. In Emacs Lisp, the symbol @code{nil} is both a -symbol and a list with no elements. For convenience, the symbol -@code{nil} is considered to have @code{nil} as its @sc{cdr} (and also -as its @sc{car}). +@dfn{proper list}@footnote{It is sometimes also referred to as a +@dfn{true list}, but we generally do not use this terminology in this +manual.}. In Emacs Lisp, the symbol @code{nil} is both a symbol and a +list with no elements. For convenience, the symbol @code{nil} is +considered to have @code{nil} as its @sc{cdr} (and also as its +@sc{car}). - Hence, the @sc{cdr} of a true list is always a true list. The -@sc{cdr} of a nonempty true list is a true list containing all the + Hence, the @sc{cdr} of a proper list is always a proper list. The +@sc{cdr} of a nonempty proper list is a proper list containing all the elements except the first. @cindex dotted list @@ -71,10 +74,10 @@ Cons Cells @sc{cdr} could point to one of the previous cons cells in the list. We call that structure a @dfn{circular list}. - For some purposes, it does not matter whether a list is true, + For some purposes, it does not matter whether a list is proper, circular or dotted. If a program doesn't look far enough down the list to see the @sc{cdr} of the final cons cell, it won't care. -However, some functions that operate on lists demand true lists and +However, some functions that operate on lists demand proper lists and signal errors if given a dotted list. Most functions that try to find the end of a list enter infinite loops if given a circular list. @@ -538,7 +541,7 @@ Building Lists is itself a list, then its elements become in effect elements of the result list. If the final element is not a list, the result is a dotted list since its final @sc{cdr} is not @code{nil} as required -in a true list. +in a proper list (@pxref{Cons Cells}). @end defun Here is an example of using @code{append}: diff --git a/doc/lispref/sequences.texi b/doc/lispref/sequences.texi index 777b1cbbff..62d3305fcf 100644 --- a/doc/lispref/sequences.texi +++ b/doc/lispref/sequences.texi @@ -1353,7 +1353,7 @@ Vector Functions @defun vconcat &rest sequences @cindex copying vectors This function returns a new vector containing all the elements of -@var{sequences}. The arguments @var{sequences} may be true lists, +@var{sequences}. The arguments @var{sequences} may be proper lists, vectors, strings or bool-vectors. If no @var{sequences} are given, the empty vector is returned. -- 2.18.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #4: 0003-Rearrange-definition-of-zerop-in-subr.el.patch --] [-- Type: text/x-diff, Size: 1419 bytes --] From 6dfbd3acf2c842583289c544008ee67744b3e794 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Fri, 6 Jul 2018 00:56:45 +0300 Subject: [PATCH 3/3] ; Rearrange definition of zerop in subr.el * lisp/subr.el (zerop): Move from under 'List functions' heading to under 'Basic Lisp functions' heading. --- lisp/subr.el | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lisp/subr.el b/lisp/subr.el index c1d90e3fb1..10343e69db 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -359,6 +359,13 @@ apply-partially (lambda (&rest args2) (apply fun (append args args2)))) +(defun zerop (number) + "Return t if NUMBER is zero." + ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because + ;; = has a byte-code. + (declare (compiler-macro (lambda (_) `(= 0 ,number)))) + (= 0 number)) + \f ;;;; List functions. @@ -548,13 +555,6 @@ nbutlast (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil)) list)))) -(defun zerop (number) - "Return t if NUMBER is zero." - ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because - ;; = has a byte-code. - (declare (compiler-macro (lambda (_) `(= 0 ,number)))) - (= 0 number)) - (defun proper-list-p (object) "Return OBJECT's length if it is a proper list, nil otherwise. A proper list is neither circular nor dotted (i.e., its last cdr -- 2.18.0 ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-09 19:40 ` Basil L. Contovounesios @ 2018-07-10 2:02 ` Paul Eggert 2018-07-10 5:46 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Paul Eggert @ 2018-07-10 2:02 UTC (permalink / raw) To: Basil L. Contovounesios, Eli Zaretskii; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 116 bytes --] Thanks, I installed that into master, and followed up with the attached minor cleanup in the general neighborhood. [-- Attachment #2: 0001-lisp-format.el-format-annotate-single-property-chang.patch --] [-- Type: text/x-patch, Size: 895 bytes --] From 7956862ca5e8f1a795cc0cec0ebe97801c3b1db7 Mon Sep 17 00:00:00 2001 From: Paul Eggert <eggert@cs.ucla.edu> Date: Mon, 9 Jul 2018 18:59:58 -0700 Subject: [PATCH] * lisp/format.el (format-annotate-single-property-change): Simplify. --- lisp/format.el | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lisp/format.el b/lisp/format.el index 1222abb..5bf1be3 100644 --- a/lisp/format.el +++ b/lisp/format.el @@ -1000,9 +1000,7 @@ format-annotate-single-property-change (if (not (and (proper-list-p old) (proper-list-p new))) (format-annotate-atomic-property-change prop-alist old new) - (let* ((old (if (listp old) old (list old))) - (new (if (listp new) new (list new))) - close open) + (let (close open) (while old (setq close (append (car (format-annotate-atomic-property-change -- 2.7.4 ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-10 2:02 ` Paul Eggert @ 2018-07-10 5:46 ` Basil L. Contovounesios 2018-07-11 3:02 ` Paul Eggert 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-10 5:46 UTC (permalink / raw) To: Paul Eggert; +Cc: Eli Zaretskii, emacs-devel Paul Eggert <eggert@cs.ucla.edu> writes: > Thanks, I installed that into master, and followed up with the attached minor > cleanup in the general neighborhood. Thanks. I assume you left the second patch (and less relevant third patch) for Eli to discuss/push out of respect/politeness. :) -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-10 5:46 ` Basil L. Contovounesios @ 2018-07-11 3:02 ` Paul Eggert 2018-07-11 6:27 ` Basil L. Contovounesios ` (2 more replies) 0 siblings, 3 replies; 100+ messages in thread From: Paul Eggert @ 2018-07-11 3:02 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: Eli Zaretskii, emacs-devel Basil L. Contovounesios wrote: > I assume you left the second patch (and less relevant third patch) for > Eli to discuss/push out of respect/politeness. :) Naahhh, I simply overlooked them. Sorry about that. I installed the 2nd patch into the emacs-26 branch (since it's just documentation wording cleanup) and the 3rd one into master. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-11 3:02 ` Paul Eggert @ 2018-07-11 6:27 ` Basil L. Contovounesios 2018-07-15 22:55 ` Wilfred Hughes 2018-07-11 14:01 ` [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el Karl Fogel 2019-04-09 12:51 ` Predicate for true lists Basil L. Contovounesios 2 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-11 6:27 UTC (permalink / raw) To: Paul Eggert; +Cc: Eli Zaretskii, emacs-devel Paul Eggert <eggert@cs.ucla.edu> writes: > Basil L. Contovounesios wrote: >> I assume you left the second patch (and less relevant third patch) for >> Eli to discuss/push out of respect/politeness. :) > > Naahhh, I simply overlooked them. Sorry about that. I installed the 2nd patch > into the emacs-26 branch (since it's just documentation wording cleanup) and the > 3rd one into master. No worries, and thanks! -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-11 6:27 ` Basil L. Contovounesios @ 2018-07-15 22:55 ` Wilfred Hughes 2018-07-16 1:37 ` Paul Eggert 0 siblings, 1 reply; 100+ messages in thread From: Wilfred Hughes @ 2018-07-15 22:55 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: Eli Zaretskii, Paul Eggert, emacs-devel [-- Attachment #1: Type: text/plain, Size: 761 bytes --] This is a great feature and I'm glad to see it in Emacs. Did we intentionally remove format-proper-list-p? This broke one of my projects. Could we set up an alias (marked as obsolete) to prevent breakage for others? Wilfred On 11 July 2018 at 07:27, Basil L. Contovounesios <contovob@tcd.ie> wrote: > Paul Eggert <eggert@cs.ucla.edu> writes: > > > Basil L. Contovounesios wrote: > >> I assume you left the second patch (and less relevant third patch) for > >> Eli to discuss/push out of respect/politeness. :) > > > > Naahhh, I simply overlooked them. Sorry about that. I installed the 2nd > patch > > into the emacs-26 branch (since it's just documentation wording cleanup) > and the > > 3rd one into master. > > No worries, and thanks! > > -- > Basil > > [-- Attachment #2: Type: text/html, Size: 1344 bytes --] ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-15 22:55 ` Wilfred Hughes @ 2018-07-16 1:37 ` Paul Eggert 0 siblings, 0 replies; 100+ messages in thread From: Paul Eggert @ 2018-07-16 1:37 UTC (permalink / raw) To: Wilfred Hughes, Basil L. Contovounesios; +Cc: Eli Zaretskii, Emacs developers [-- Attachment #1: Type: text/plain, Size: 290 bytes --] On 07/15/2018 05:55 PM, Wilfred Hughes wrote: > Did we intentionally remove format-proper-list-p? This broke one of my > projects. I did, yes. I thought it obvious that it was intended only for use within format.el. Evidently not, so I added an obsolete aiias by installing the attached. [-- Attachment #2: 0001-lisp-format.el-format-proper-list-p-New-alias.patch --] [-- Type: text/x-patch, Size: 1229 bytes --] From f521161c1bc5a9cd10ee25ff5f4b7b8d753db55d Mon Sep 17 00:00:00 2001 From: Paul Eggert <eggert@Penguin.CS.UCLA.EDU> Date: Sun, 15 Jul 2018 18:28:35 -0700 Subject: [PATCH] * lisp/format.el (format-proper-list-p): New alias. --- etc/NEWS | 3 ++- lisp/format.el | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index c69bbe9d0f..2a93bdf025 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -713,7 +713,8 @@ manual for more details. +++ ** New function 'proper-list-p'. Given a proper list as argument, this predicate returns its length; -otherwise, it returns nil. +otherwise, it returns nil. 'format-proper-list-p' is now an obsolete +alias for the new function. ** define-minor-mode automatically documents the meaning of ARG diff --git a/lisp/format.el b/lisp/format.el index 5bf1be3947..49d3c718ab 100644 --- a/lisp/format.el +++ b/lisp/format.el @@ -539,6 +539,8 @@ Compare using `equal'." (setq tail next))) (cons acopy bcopy))) +(define-obsolete-function-alias 'format-proper-list-p 'proper-list-p "27.1") + (defun format-reorder (items order) "Arrange ITEMS to follow partial ORDER. Elements of ITEMS equal to elements of ORDER will be rearranged -- 2.17.1 ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el 2018-07-11 3:02 ` Paul Eggert 2018-07-11 6:27 ` Basil L. Contovounesios @ 2018-07-11 14:01 ` Karl Fogel 2018-07-11 17:12 ` Basil L. Contovounesios 2019-04-09 12:51 ` Predicate for true lists Basil L. Contovounesios 2 siblings, 1 reply; 100+ messages in thread From: Karl Fogel @ 2018-07-11 14:01 UTC (permalink / raw) To: emacs-devel Paul Eggert <eggert@cs.ucla.edu> wrote on emacs-devel: >Naahhh, I simply overlooked them. Sorry about that. I installed the >2nd patch into the emacs-26 branch (since it's just documentation >wording cleanup) and the 3rd one into master. Hunh -- I'm not sure why, but that 3rd patch seems to be leading to build lossage on 'master'. Quoting below from 'emacs-diffs': >branch: master >commit babe0d4508273c5fe0a3228b3d2b4d3dcb72cd58 >Author: Basil L. Contovounesios <address@hidden> >Commit: Paul Eggert <address@hidden> > > ; Rearrange definition of zerop in subr.el > > * lisp/subr.el (zerop): Move from under 'List functions' heading to > under 'Basic Lisp functions' heading. >--- > lisp/subr.el | 14 +++++++------- > 1 file changed, 7 insertions(+), 7 deletions(-) > >diff --git a/lisp/subr.el b/lisp/subr.el >index c1d90e3..10343e6 100644 >--- a/lisp/subr.el >+++ b/lisp/subr.el >@@ -359,6 +359,13 @@ was called." > (lambda (&rest args2) > (apply fun (append args args2)))) > >+(defun zerop (number) >+ "Return t if NUMBER is zero." >+ ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because >+ ;; = has a byte-code. >+ (declare (compiler-macro (lambda (_) `(= 0 ,number)))) >+ (= 0 number)) >+ > \f > ;;;; List functions. > >@@ -548,13 +555,6 @@ If N is omitted or nil, remove the last element." > (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil)) > list)))) > >-(defun zerop (number) >- "Return t if NUMBER is zero." >- ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because >- ;; = has a byte-code. >- (declare (compiler-macro (lambda (_) `(= 0 ,number)))) >- (= 0 number)) >- > (defun proper-list-p (object) > "Return OBJECT's length if it is a proper list, nil otherwise. > A proper list is neither circular nor dotted (i.e., its last cdr Here's the build failure that seems to happen with that commit: $ git checkout master Switched to branch 'master' Your branch is up to date with 'origin/master'. $ git log --name-status -n 2 commit babe0d4508273c5fe0a3228b3d2b4d3dcb72cd58 (HEAD -> master, origin/master, origin/HEAD) Author: Basil L. Contovounesios <contovob@tcd.ie> Date: Tue Jul 10 23:08:58 2018 -0700 ; Rearrange definition of zerop in subr.el * lisp/subr.el (zerop): Move from under 'List functions' heading to under 'Basic Lisp functions' heading. M lisp/subr.el commit f8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644 Author: Stefan Monnier <monnier@iro.umontreal.ca> Date: Tue Jul 10 22:52:21 2018 -0400 * lisp/vc/diff-mode.el: Perform hunk refinement from font-lock Remove redundant :group arguments. (diff-font-lock-refine): New var. (diff--refine-hunk): New function, extracted from diff-refine-hunk. (diff-refine-hunk): Use it. (diff--font-lock-refine--refresh): New function. (diff--font-lock-refined): New function. (diff-font-lock-keywords): Use it. M lisp/vc/diff-mode.el $ make distclean $ ./autogen.sh && ./configure --with-mailutils $ make bootstrap [...much output omitted...] CCLD temacs /bin/mkdir -p ../etc setfattr -n user.pax.flags -v er temacs make -C ../lisp update-subdirs make[3]: Entering directory '/home/kfogel/src/emacs/lisp' make[3]: Leaving directory '/home/kfogel/src/emacs/lisp' ./temacs --batch --load loadup bootstrap Loading loadup.el (source)... Using load-path (/home/kfogel/src/emacs/lisp /home/kfogel/src/emacs/lisp/emacs-lisp /home/kfogel/src/emacs/lisp/progmodes /home/kfogel/src/emacs/lisp/language /home/kfogel/src/emacs/lisp/international /home/kfogel/src/emacs/lisp/textmodes /home/kfogel/src/emacs/lisp/vc) Loading emacs-lisp/byte-run (source)... Loading emacs-lisp/backquote (source)... Loading subr (source)... Symbol’s function definition is void: cadr Makefile:746: recipe for target 'bootstrap-emacs' failed make[2]: *** [bootstrap-emacs] Error 255 make[2]: Leaving directory '/home/kfogel/src/emacs/src' Makefile:418: recipe for target 'src' failed make[1]: *** [src] Error 2 make[1]: Leaving directory '/home/kfogel/src/emacs' Makefile:1101: recipe for target 'bootstrap' failed make: *** [bootstrap] Error 2 $ But if I revert to the previous revision, the build succeeds: $ git checkout f8b1e40fb63b0a6bc6692cc0bc84e5f5e65c2644 $ git status HEAD detached at f8b1e40fb6 nothing to commit, working tree clean $ make distclean $ ./autogen.sh && ./configure --with-mailutils $ make bootstrap [... no problems ...] $ I won't have time to look at this further for probably the rest of today, so am posting in case anyone sees the problem right away. Best regards, -Karl ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el 2018-07-11 14:01 ` [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el Karl Fogel @ 2018-07-11 17:12 ` Basil L. Contovounesios 2018-07-11 17:33 ` Paul Eggert 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-11 17:12 UTC (permalink / raw) To: Karl Fogel; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 1197 bytes --] Karl Fogel <kfogel@red-bean.com> writes: > Paul Eggert <eggert@cs.ucla.edu> wrote on emacs-devel: >>Naahhh, I simply overlooked them. Sorry about that. I installed the >>2nd patch into the emacs-26 branch (since it's just documentation >>wording cleanup) and the 3rd one into master. > > Hunh -- I'm not sure why, but that 3rd patch seems to be leading to build > lossage on 'master'. Quoting below from 'emacs-diffs': > I won't have time to look at this further for probably the rest of today, so am posting in case anyone sees the problem right away. I see Glenn has already reverted the problematic commit [78125f3744], thanks Glenn! I'm sorry I didn't try to build myself before suggesting the change. [78125f3744]: 2018-07-11 08:27:14 -0700 Unbreak bootstrap https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=78125f37444acd4f1ec4a0a5b0a338d80672f2ec Something about (declare (compiler-macro (lambda (_) `(= 0 ,number)))) requires both cadr and cddr, and it's not the backquote. My first thought is that it relates to the compiler-macro and its expansion by macroexp.el; can anyone confirm this or correct me? Would adding a comment along the following lines be welcome? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Add-commentary-on-location-of-zerop.patch --] [-- Type: text/x-diff, Size: 1205 bytes --] From 68d2cc3bb635e117603eb41fc892c9c92a625fb9 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Wed, 11 Jul 2018 20:11:55 +0300 Subject: [PATCH] ; Add commentary on location of zerop * lisp/subr.el (zerop): Add commentary explaining why moving the function's location within the file broke bootstrap in 2018-07-10T23:08:58-07:00!eggert@cs.ucla.edu [babe0d4508]. [babe0d4508]: 2018-07-10 23:08:58 -0700 ; Rearrange definition of zerop in subr.el https://git.savannah.gnu.org/cgit/emacs.git/\ commit/?id=babe0d4508273c5fe0a3228b3d2b4d3dcb72cd58 --- lisp/subr.el | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lisp/subr.el b/lisp/subr.el index c1d90e3fb1..a5108eb655 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -548,6 +548,9 @@ nbutlast (if (> n 0) (setcdr (nthcdr (- (1- m) n) list) nil)) list)))) +;; This function appears here instead of under the 'Basic Lisp +;; functions' heading because during bootstrap its compiler-macro +;; requires functions defined under the 'List functions' heading. (defun zerop (number) "Return t if NUMBER is zero." ;; Used to be in C, but it's pointless since (= 0 n) is faster anyway because -- 2.18.0 [-- Attachment #3: Type: text/plain, Size: 20 bytes --] Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el 2018-07-11 17:12 ` Basil L. Contovounesios @ 2018-07-11 17:33 ` Paul Eggert 2018-07-12 15:34 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Paul Eggert @ 2018-07-11 17:33 UTC (permalink / raw) To: Basil L. Contovounesios, Karl Fogel; +Cc: emacs-devel Basil L. Contovounesios wrote: > Would adding a comment along the following lines be welcome? Yes, it might help avoid future such problems. I installed it (after removing some of the excessive text in the commit message and fixing the attribution). I had tested the earlier patch that broke 'make bootstrap', but only with 'make check' not with 'make bootstrap check'. (And even 'make check' is becoming too much of a burden nowadays, what with the Tramp tests taking so long....) ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el 2018-07-11 17:33 ` Paul Eggert @ 2018-07-12 15:34 ` Basil L. Contovounesios 2018-07-12 15:43 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-12 15:34 UTC (permalink / raw) To: Paul Eggert; +Cc: Karl Fogel, emacs-devel Paul Eggert <eggert@cs.ucla.edu> writes: > Basil L. Contovounesios wrote: > >> Would adding a comment along the following lines be welcome? > > Yes, it might help avoid future such problems. I installed it (after removing > some of the excessive text in the commit message and fixing the attribution). Thanks. Re: the attribution, I must have misinterpreted the following excerpt from CONTRIBUTE: One way to identify revisions is by quoting their summary line. Another is with an action stamp - an RFC3339 date followed by ! followed by the committer's email - for example, "2014-01-16T05:43:35Z!esr@thyrsus.com". I initially paired the git-log fields CommitDate and Commit, rather than AuthorDate and Author, respectively (hence your email in place of mine), whereas you paired CommitDate and Author. I realise this is probably inconsequential or overly pedantic, but I'm nevertheless curious whether there's any difference or preference. -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el 2018-07-12 15:34 ` Basil L. Contovounesios @ 2018-07-12 15:43 ` Basil L. Contovounesios 0 siblings, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-12 15:43 UTC (permalink / raw) To: Paul Eggert; +Cc: Karl Fogel, emacs-devel "Basil L. Contovounesios" <contovob@tcd.ie> writes: > I initially paired the git-log fields CommitDate and Commit, rather than > AuthorDate and Author, respectively (hence your email in place of mine), > whereas you paired CommitDate and Author. Oops, I was looking at the wrong revision when I wrote this; CommitDate and AuthorDate are equivalent in your amended revision. So I take it AuthorDate and Author are preferred over CommitDate and Commit, respectively, and this makes sense. Sorry about the noise, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-11 3:02 ` Paul Eggert 2018-07-11 6:27 ` Basil L. Contovounesios 2018-07-11 14:01 ` [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el Karl Fogel @ 2019-04-09 12:51 ` Basil L. Contovounesios 2019-04-09 15:33 ` Stefan Monnier 2 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-09 12:51 UTC (permalink / raw) To: Paul Eggert; +Cc: Eli Zaretskii, emacs-devel [-- Attachment #1: Type: text/plain, Size: 301 bytes --] The byte-compiler currently optimises various constant expressions, for example: (byte-compile '(string-equal "" "")) ;; => t IWBNI this was also done for proper-list-p: (byte-compile '(proper-list-p '(0))) ;; => (byte-code "\300\301!\207" [proper-list-p (0)] 2) Is the following kosher? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Make-byte-compiler-aware-of-proper-list-p.patch --] [-- Type: text/x-diff, Size: 2252 bytes --] From 7a1da041ac6053ec752c3f31072203195c4e24db Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Tue, 9 Apr 2019 13:01:52 +0100 Subject: [PATCH] Make byte-compiler aware of proper-list-p * lisp/emacs-lisp/byte-opt.el: Optimize proper-list-p as a predicate and mark it as side-effect-free. * lisp/emacs-lisp/bytecomp.el: Add one-arg compiler-form for proper-list-p. --- lisp/emacs-lisp/byte-opt.el | 7 ++++--- lisp/emacs-lisp/bytecomp.el | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 33d4964763..4577297bba 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -879,7 +879,8 @@ byte-optimize-memq (put 'symbolp 'byte-optimizer 'byte-optimize-predicate) (put 'stringp 'byte-optimizer 'byte-optimize-predicate) (put 'string< 'byte-optimizer 'byte-optimize-predicate) -(put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) +(put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) +(put 'proper-list-p 'byte-optimizer 'byte-optimize-predicate) (put 'logand 'byte-optimizer 'byte-optimize-predicate) (put 'logior 'byte-optimizer 'byte-optimize-predicate) @@ -1160,8 +1161,8 @@ byte-optimize-set make-list make-string make-symbol marker-buffer max member memq min minibuffer-selected-window minibuffer-window mod multibyte-char-to-unibyte next-window nth nthcdr number-to-string - parse-colon-path plist-get plist-member - prefix-numeric-value previous-window prin1-to-string propertize + parse-colon-path plist-get plist-member prefix-numeric-value + previous-window prin1-to-string proper-list-p propertize degrees-to-radians radians-to-degrees rassq rassoc read-from-string regexp-quote region-beginning region-end reverse round diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 8bbe6292d9..0e953dc96b 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -3566,6 +3566,7 @@ byte-defop-compiler-1 ;;####(byte-defop-compiler move-to-column 1) (byte-defop-compiler-1 interactive byte-compile-noop) +(byte-defop-compiler-1 proper-list-p 1) \f (defun byte-compile-subr-wrong-args (form n) -- 2.20.1 [-- Attachment #3: Type: text/plain, Size: 20 bytes --] Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 12:51 ` Predicate for true lists Basil L. Contovounesios @ 2019-04-09 15:33 ` Stefan Monnier 2019-04-09 16:20 ` Basil L. Contovounesios 2019-04-09 20:12 ` Predicate for true lists Basil L. Contovounesios 0 siblings, 2 replies; 100+ messages in thread From: Stefan Monnier @ 2019-04-09 15:33 UTC (permalink / raw) To: emacs-devel > IWBNI this was also done for proper-list-p: > > (byte-compile '(proper-list-p '(0))) > ;; => (byte-code "\300\301!\207" [proper-list-p (0)] 2) > > Is the following kosher? > @@ -879,7 +879,8 @@ byte-optimize-memq > (put 'symbolp 'byte-optimizer 'byte-optimize-predicate) > (put 'stringp 'byte-optimizer 'byte-optimize-predicate) > (put 'string< 'byte-optimizer 'byte-optimize-predicate) > -(put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) > +(put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) > +(put 'proper-list-p 'byte-optimizer 'byte-optimize-predicate) > > (put 'logand 'byte-optimizer 'byte-optimize-predicate) > (put 'logior 'byte-optimizer 'byte-optimize-predicate) I think this is currently right. > @@ -1160,8 +1161,8 @@ byte-optimize-set > make-list make-string make-symbol marker-buffer max member memq min > minibuffer-selected-window minibuffer-window > mod multibyte-char-to-unibyte next-window nth nthcdr number-to-string > - parse-colon-path plist-get plist-member > - prefix-numeric-value previous-window prin1-to-string propertize > + parse-colon-path plist-get plist-member prefix-numeric-value > + previous-window prin1-to-string proper-list-p propertize > degrees-to-radians > radians-to-degrees rassq rassoc read-from-string regexp-quote > region-beginning region-end reverse round I think it'd be better to add a `side-effect-free` property rather than add to this list. We should be moving away from having this list in byte-opt.el since that info can also be used when byte-opt is not loaded. > diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el > index 8bbe6292d9..0e953dc96b 100644 > --- a/lisp/emacs-lisp/bytecomp.el > +++ b/lisp/emacs-lisp/bytecomp.el > @@ -3566,6 +3566,7 @@ byte-defop-compiler-1 > > ;;####(byte-defop-compiler move-to-column 1) > (byte-defop-compiler-1 interactive byte-compile-noop) > +(byte-defop-compiler-1 proper-list-p 1) I don't think this is needed. Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 15:33 ` Stefan Monnier @ 2019-04-09 16:20 ` Basil L. Contovounesios 2019-04-09 16:32 ` Stefan Monnier 2019-04-09 20:12 ` Predicate for true lists Basil L. Contovounesios 1 sibling, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-09 16:20 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: 0001-Optimize-byte-compilation-of-proper-list-p.patch --] [-- Type: text/x-diff, Size: 1888 bytes --] From 430fcb473608a4501826a982a29efa8c31ed6c5c Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Tue, 9 Apr 2019 16:51:55 +0100 Subject: [PATCH 1/2] Optimize byte-compilation of proper-list-p * lisp/emacs-lisp/byte-opt.el: Optimize proper-list-p as a predicate. * src/fns.c (syms_of_fns): Mark proper-list-p as pure and side-effect-free. --- lisp/emacs-lisp/byte-opt.el | 3 ++- src/fns.c | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 33d4964763..44cca6136c 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -879,7 +879,8 @@ byte-optimize-memq (put 'symbolp 'byte-optimizer 'byte-optimize-predicate) (put 'stringp 'byte-optimizer 'byte-optimize-predicate) (put 'string< 'byte-optimizer 'byte-optimize-predicate) -(put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) +(put 'string-lessp 'byte-optimizer 'byte-optimize-predicate) +(put 'proper-list-p 'byte-optimizer 'byte-optimize-predicate) (put 'logand 'byte-optimizer 'byte-optimize-predicate) (put 'logior 'byte-optimizer 'byte-optimize-predicate) diff --git a/src/fns.c b/src/fns.c index c3202495da..c840019456 100644 --- a/src/fns.c +++ b/src/fns.c @@ -5326,6 +5326,12 @@ syms_of_fns (void) DEFSYM (Qcursor_in_echo_area, "cursor-in-echo-area"); DEFSYM (Qwidget_type, "widget-type"); + DEFSYM (Qpure, "pure"); + DEFSYM (Qside_effect_free, "side-effect-free"); + DEFSYM (Qproper_list_p, "proper-list-p"); + Fput (Qproper_list_p, Qpure, Qt); + Fput (Qproper_list_p, Qside_effect_free, Qt); + DEFVAR_LISP ("overriding-plist-environment", Voverriding_plist_environment, doc: /* An alist that overrides the plists of the symbols which it lists. Used by the byte-compiler to apply `define-symbol-prop' during -- 2.20.1 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0002-Move-proper-list-p-tests-to-fns-tests.el.patch --] [-- Type: text/x-diff, Size: 2750 bytes --] From 82399337d891f8c078b78f0de013ba240d1fc236 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Tue, 9 Apr 2019 17:05:27 +0100 Subject: [PATCH 2/2] Move proper-list-p tests to fns-tests.el This follows the move of proper-list-p from lisp/subr.el to src/fns.c in 2018-07-24T15:58:46-07:00!eggert@cs.ucla.edu. * test/lisp/subr-tests.el (subr-tests--proper-list-p): Move from here... * test/src/fns-tests.el (test-proper-list-p): ...to here. --- test/lisp/subr-tests.el | 18 ------------------ test/src/fns-tests.el | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el index 7465aac5ea..c458eef2f9 100644 --- a/test/lisp/subr-tests.el +++ b/test/lisp/subr-tests.el @@ -318,24 +318,6 @@ subr-test--frames-1 (should (eq (string-to-char (symbol-name (gensym))) ?g)) (should (eq (string-to-char (symbol-name (gensym "X"))) ?X))) -(ert-deftest subr-tests--proper-list-p () - "Test `proper-list-p' behavior." - (dotimes (length 4) - ;; Proper and dotted lists. - (let ((list (make-list length 0))) - (should (= (proper-list-p list) length)) - (should (not (proper-list-p (nconc list 0))))) - ;; Circular lists. - (dotimes (n (1+ length)) - (let ((circle (make-list (1+ length) 0))) - (should (not (proper-list-p (nconc circle (nthcdr n circle)))))))) - ;; Atoms. - (should (not (proper-list-p 0))) - (should (not (proper-list-p ""))) - (should (not (proper-list-p []))) - (should (not (proper-list-p (make-bool-vector 0 nil)))) - (should (not (proper-list-p (make-symbol "a"))))) - (ert-deftest subr-tests--assq-delete-all () "Test `assq-delete-all' behavior." (cl-flet ((new-list-fn diff --git a/test/src/fns-tests.el b/test/src/fns-tests.el index d6cc99e8e3..6ebab4287f 100644 --- a/test/src/fns-tests.el +++ b/test/src/fns-tests.el @@ -648,4 +648,22 @@ dot2 (should (equal (list (eq a b) n len) (list t n len)))))))) +(ert-deftest test-proper-list-p () + "Test `proper-list-p' behavior." + (dotimes (length 4) + ;; Proper and dotted lists. + (let ((list (make-list length 0))) + (should (= (proper-list-p list) length)) + (should (not (proper-list-p (nconc list 0))))) + ;; Circular lists. + (dotimes (n (1+ length)) + (let ((circle (make-list (1+ length) 0))) + (should (not (proper-list-p (nconc circle (nthcdr n circle)))))))) + ;; Atoms. + (should (not (proper-list-p 0))) + (should (not (proper-list-p ""))) + (should (not (proper-list-p []))) + (should (not (proper-list-p (make-bool-vector 0 nil)))) + (should (not (proper-list-p (make-symbol "a"))))) + (provide 'fns-tests) -- 2.20.1 [-- Attachment #3: Type: text/plain, Size: 1792 bytes --] Stefan Monnier <monnier@iro.umontreal.ca> writes: >> @@ -1160,8 +1161,8 @@ byte-optimize-set >> make-list make-string make-symbol marker-buffer max member memq min >> minibuffer-selected-window minibuffer-window >> mod multibyte-char-to-unibyte next-window nth nthcdr number-to-string >> - parse-colon-path plist-get plist-member >> - prefix-numeric-value previous-window prin1-to-string propertize >> + parse-colon-path plist-get plist-member prefix-numeric-value >> + previous-window prin1-to-string proper-list-p propertize >> degrees-to-radians >> radians-to-degrees rassq rassoc read-from-string regexp-quote >> region-beginning region-end reverse round > > I think it'd be better to add a `side-effect-free` property rather than > add to this list. We should be moving away from having this list in > byte-opt.el since that info can also be used when byte-opt is not > loaded. Makes sense, how's the attached? Is there a better place to do this than in syms_of_fns? (I followed the example in src/json.c.) >> diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el >> index 8bbe6292d9..0e953dc96b 100644 >> --- a/lisp/emacs-lisp/bytecomp.el >> +++ b/lisp/emacs-lisp/bytecomp.el >> @@ -3566,6 +3566,7 @@ byte-defop-compiler-1 >> >> ;;####(byte-defop-compiler move-to-column 1) >> (byte-defop-compiler-1 interactive byte-compile-noop) >> +(byte-defop-compiler-1 proper-list-p 1) > > I don't think this is needed. Right. For some reason the byte-compiler wasn't complaining about wrong numbers of arguments without this compiler-form, but I can no longer reproduce that. I also attach a second patch which moves the proper-list-p tests to the correct file following the function's rewriting in C. WDYT? Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 16:20 ` Basil L. Contovounesios @ 2019-04-09 16:32 ` Stefan Monnier 2019-04-09 16:54 ` Daniel Colascione ` (2 more replies) 0 siblings, 3 replies; 100+ messages in thread From: Stefan Monnier @ 2019-04-09 16:32 UTC (permalink / raw) To: emacs-devel > --- a/src/fns.c > +++ b/src/fns.c > @@ -5326,6 +5326,12 @@ syms_of_fns (void) > DEFSYM (Qcursor_in_echo_area, "cursor-in-echo-area"); > DEFSYM (Qwidget_type, "widget-type"); > > + DEFSYM (Qpure, "pure"); > + DEFSYM (Qside_effect_free, "side-effect-free"); > + DEFSYM (Qproper_list_p, "proper-list-p"); > + Fput (Qproper_list_p, Qpure, Qt); > + Fput (Qproper_list_p, Qside_effect_free, Qt); I think you can set the side-effect-free property to `error-free`, which is stronger. Other than that, it looks OK (tho I personally find doing it in C to be too painful, so I prefer moving this kind of code to subr.el). > I also attach a second patch which moves the proper-list-p tests to the > correct file following the function's rewriting in C. WDYT? LGTM, Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 16:32 ` Stefan Monnier @ 2019-04-09 16:54 ` Daniel Colascione 2019-04-09 17:27 ` Basil L. Contovounesios 2019-04-09 17:27 ` Basil L. Contovounesios 2019-04-09 20:08 ` Unused value of error-free function warning (was: Predicate for true lists) Basil L. Contovounesios 2 siblings, 1 reply; 100+ messages in thread From: Daniel Colascione @ 2019-04-09 16:54 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel >> --- a/src/fns.c >> +++ b/src/fns.c >> @@ -5326,6 +5326,12 @@ syms_of_fns (void) >> DEFSYM (Qcursor_in_echo_area, "cursor-in-echo-area"); >> DEFSYM (Qwidget_type, "widget-type"); >> >> + DEFSYM (Qpure, "pure"); >> + DEFSYM (Qside_effect_free, "side-effect-free"); >> + DEFSYM (Qproper_list_p, "proper-list-p"); >> + Fput (Qproper_list_p, Qpure, Qt); >> + Fput (Qproper_list_p, Qside_effect_free, Qt); > > I think you can set the side-effect-free property to `error-free`, which > is stronger. Other than that, it looks OK (tho I personally find doing it > in C to be too painful, so I prefer moving this kind of code to subr.el). > >> I also attach a second patch which moves the proper-list-p tests to the >> correct file following the function's rewriting in C. WDYT? I'd also prefer to put new functionality in lisp when possible. We're going to get a JIT one day, and when we do, lisp functionality may very well end up *faster* than C stuff anyway due to specialization and tracing. In the meantime, lisp performance is adequate, and if we can keep the C core small, we should. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 16:54 ` Daniel Colascione @ 2019-04-09 17:27 ` Basil L. Contovounesios 0 siblings, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-09 17:27 UTC (permalink / raw) To: Daniel Colascione; +Cc: Stefan Monnier, emacs-devel "Daniel Colascione" <dancol@dancol.org> writes: >>> --- a/src/fns.c >>> +++ b/src/fns.c >>> @@ -5326,6 +5326,12 @@ syms_of_fns (void) >>> DEFSYM (Qcursor_in_echo_area, "cursor-in-echo-area"); >>> DEFSYM (Qwidget_type, "widget-type"); >>> >>> + DEFSYM (Qpure, "pure"); >>> + DEFSYM (Qside_effect_free, "side-effect-free"); >>> + DEFSYM (Qproper_list_p, "proper-list-p"); >>> + Fput (Qproper_list_p, Qpure, Qt); >>> + Fput (Qproper_list_p, Qside_effect_free, Qt); >> >> I think you can set the side-effect-free property to `error-free`, which >> is stronger. Other than that, it looks OK (tho I personally find doing it >> in C to be too painful, so I prefer moving this kind of code to subr.el). >> >>> I also attach a second patch which moves the proper-list-p tests to the >>> correct file following the function's rewriting in C. WDYT? > > I'd also prefer to put new functionality in lisp when possible. We're > going to get a JIT one day, and when we do, lisp functionality may very > well end up *faster* than C stuff anyway due to specialization and > tracing. In the meantime, lisp performance is adequate, and if we can keep > the C core small, we should. The function proper-list-p was originally added to subr.el. My understanding is Paul moved it to fns.c not for a speedup, but so that other C code could use it[1]. [1: 200195e824]: Move proper-list-p to C 2018-07-24 16:08:09 -0700 https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=200195e824befa112459c0afbac7c94aea739573 -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 16:32 ` Stefan Monnier 2019-04-09 16:54 ` Daniel Colascione @ 2019-04-09 17:27 ` Basil L. Contovounesios 2019-04-09 20:08 ` Unused value of error-free function warning (was: Predicate for true lists) Basil L. Contovounesios 2 siblings, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-09 17:27 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: >> --- a/src/fns.c >> +++ b/src/fns.c >> @@ -5326,6 +5326,12 @@ syms_of_fns (void) >> DEFSYM (Qcursor_in_echo_area, "cursor-in-echo-area"); >> DEFSYM (Qwidget_type, "widget-type"); >> >> + DEFSYM (Qpure, "pure"); >> + DEFSYM (Qside_effect_free, "side-effect-free"); >> + DEFSYM (Qproper_list_p, "proper-list-p"); >> + Fput (Qproper_list_p, Qpure, Qt); >> + Fput (Qproper_list_p, Qside_effect_free, Qt); > > I think you can set the side-effect-free property to `error-free`, which > is stronger. Okay (I think the make_fixnum in Fproper_list_p made me think it was allocating memory). > Other than that, it looks OK (tho I personally find doing it in C to > be too painful, so I prefer moving this kind of code to subr.el). I'll move it to subr.el then if no-one else objects. >> I also attach a second patch which moves the proper-list-p tests to the >> correct file following the function's rewriting in C. WDYT? > > LGTM, Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Unused value of error-free function warning (was: Predicate for true lists) 2019-04-09 16:32 ` Stefan Monnier 2019-04-09 16:54 ` Daniel Colascione 2019-04-09 17:27 ` Basil L. Contovounesios @ 2019-04-09 20:08 ` Basil L. Contovounesios 2019-04-09 20:40 ` Unused value of error-free function warning Stefan Monnier 2 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-09 20:08 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: >> --- a/src/fns.c >> +++ b/src/fns.c >> @@ -5326,6 +5326,12 @@ syms_of_fns (void) >> DEFSYM (Qcursor_in_echo_area, "cursor-in-echo-area"); >> DEFSYM (Qwidget_type, "widget-type"); >> >> + DEFSYM (Qpure, "pure"); >> + DEFSYM (Qside_effect_free, "side-effect-free"); >> + DEFSYM (Qproper_list_p, "proper-list-p"); >> + Fput (Qproper_list_p, Qpure, Qt); >> + Fput (Qproper_list_p, Qside_effect_free, Qt); > > I think you can set the side-effect-free property to `error-free`, which > is stronger. If error-free functions are even more restricted than other side-effect-free ones, then why does the former class not raise a warning in byte-optimize-form-code-walker when their return value remains unused? For example, compare: (byte-compile '(progn (list) t)) ;; => t with: (byte-compile '(progn (+) t)) ;; => t (Warning: value returned from (+) is unused) I understand that dead error-free expressions are just deleted, but shouldn't the user be warned of this? Or is that what "error-free" stands for? -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Unused value of error-free function warning 2019-04-09 20:08 ` Unused value of error-free function warning (was: Predicate for true lists) Basil L. Contovounesios @ 2019-04-09 20:40 ` Stefan Monnier 0 siblings, 0 replies; 100+ messages in thread From: Stefan Monnier @ 2019-04-09 20:40 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: emacs-devel > (byte-compile '(progn (list) t)) > ;; => t > > with: > > (byte-compile '(progn (+) t)) > ;; => t (Warning: value returned from (+) is unused) IIRC part of the reason is that it's safe to remove (list) so it's a plain and simple optimization and the user may consciously rely on it (e.g., I remember changing some `car` calls to `car-safe` in some macros so as not to get warnings when the result is not used), whereas for (+) it's not guaranteed to be safe (and it's only removed depending on byte-compile-delete-errors). > I understand that dead error-free expressions are just deleted, but > shouldn't the user be warned of this? Warnings are pretty tricky business because you don't want to annoy the user with warnings about perfectly legitimate code. So there's no hard and fast rule and you can't always get it right; instead it's a murky grey area where you try to minimize both the false positives and false negatives. Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 15:33 ` Stefan Monnier 2019-04-09 16:20 ` Basil L. Contovounesios @ 2019-04-09 20:12 ` Basil L. Contovounesios 2019-04-09 20:41 ` Stefan Monnier 2019-04-10 2:32 ` Eli Zaretskii 1 sibling, 2 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-09 20:12 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel [-- Attachment #1: Type: text/plain, Size: 973 bytes --] Stefan Monnier <monnier@iro.umontreal.ca> writes: >> @@ -1160,8 +1161,8 @@ byte-optimize-set >> make-list make-string make-symbol marker-buffer max member memq min >> minibuffer-selected-window minibuffer-window >> mod multibyte-char-to-unibyte next-window nth nthcdr number-to-string >> - parse-colon-path plist-get plist-member >> - prefix-numeric-value previous-window prin1-to-string propertize >> + parse-colon-path plist-get plist-member prefix-numeric-value >> + previous-window prin1-to-string proper-list-p propertize >> degrees-to-radians >> radians-to-degrees rassq rassoc read-from-string regexp-quote >> region-beginning region-end reverse round > > I think it'd be better to add a `side-effect-free` property rather than > add to this list. We should be moving away from having this list in > byte-opt.el since that info can also be used when byte-opt is not loaded. Should the Elisp manual be updated to reflect this? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Update-side-effect-free-primitive-docs.patch --] [-- Type: text/x-diff, Size: 1519 bytes --] From fd52e0b08866ecdb93088e539423ee8af6db2fa9 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Tue, 9 Apr 2019 20:55:25 +0100 Subject: [PATCH] Update side-effect-free primitive docs * doc/lispref/internals.texi (Writing Emacs Primitives): Describe currently preferred approach to marking built-in functions as side-effect-free. --- doc/lispref/internals.texi | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 8ebe47d9ad..826818c380 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1031,10 +1031,13 @@ Writing Emacs Primitives @file{lisp.h} contains the definitions for some important macros and functions. - If you define a function which is side-effect free, update the code -in @file{byte-opt.el} that binds @code{side-effect-free-fns} and -@code{side-effect-and-error-free-fns} so that the compiler optimizer -knows about it. + If you define a function which is side-effect free, give it a +non-@code{nil} @code{side-effect-free} declaration (@pxref{Declare +Form}) or function property (@pxref{Symbol Plists}) so that the +compiler optimizer knows about it. Alternatively, you can update the +code in @file{byte-opt.el} that binds @code{side-effect-free-fns} and +@code{side-effect-and-error-free-fns}, but this approach is less +modular and no longer encouraged. @node Writing Dynamic Modules @section Writing Dynamically-Loaded Modules -- 2.20.1 [-- Attachment #3: Type: text/plain, Size: 20 bytes --] Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 20:12 ` Predicate for true lists Basil L. Contovounesios @ 2019-04-09 20:41 ` Stefan Monnier 2019-04-10 2:32 ` Eli Zaretskii 1 sibling, 0 replies; 100+ messages in thread From: Stefan Monnier @ 2019-04-09 20:41 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: emacs-devel > Should the Elisp manual be updated to reflect this? > > From fd52e0b08866ecdb93088e539423ee8af6db2fa9 Mon Sep 17 00:00:00 2001 > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Date: Tue, 9 Apr 2019 20:55:25 +0100 > Subject: [PATCH] Update side-effect-free primitive docs > > * doc/lispref/internals.texi (Writing Emacs Primitives): Describe > currently preferred approach to marking built-in functions as > side-effect-free. LGTM, thanks, Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-09 20:12 ` Predicate for true lists Basil L. Contovounesios 2019-04-09 20:41 ` Stefan Monnier @ 2019-04-10 2:32 ` Eli Zaretskii 2019-04-10 14:16 ` Alex Branham 1 sibling, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2019-04-10 2:32 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: monnier, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Date: Tue, 09 Apr 2019 21:12:49 +0100 > Cc: emacs-devel@gnu.org > > diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi > index 8ebe47d9ad..826818c380 100644 > --- a/doc/lispref/internals.texi > +++ b/doc/lispref/internals.texi > @@ -1031,10 +1031,13 @@ Writing Emacs Primitives > @file{lisp.h} contains the definitions for some important macros and > functions. > > - If you define a function which is side-effect free, update the code > -in @file{byte-opt.el} that binds @code{side-effect-free-fns} and > -@code{side-effect-and-error-free-fns} so that the compiler optimizer > -knows about it. > + If you define a function which is side-effect free, give it a > +non-@code{nil} @code{side-effect-free} declaration (@pxref{Declare > +Form}) or function property (@pxref{Symbol Plists}) so that the > +compiler optimizer knows about it. Alternatively, you can update the > +code in @file{byte-opt.el} that binds @code{side-effect-free-fns} and > +@code{side-effect-and-error-free-fns}, but this approach is less > +modular and no longer encouraged. This should have a @cindex entry for "side-effect-free" and other symbols referenced here. Bonus points for adding some minimal explanation what does "side-effect free function" mean. Thanks. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-10 2:32 ` Eli Zaretskii @ 2019-04-10 14:16 ` Alex Branham 2019-04-10 14:34 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Alex Branham @ 2019-04-10 14:16 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Basil L. Contovounesios, monnier, emacs-devel On Wed 10 Apr 2019 at 05:32, Eli Zaretskii <eliz@gnu.org> wrote: >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Date: Tue, 09 Apr 2019 21:12:49 +0100 >> Cc: emacs-devel@gnu.org >> >> diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi >> index 8ebe47d9ad..826818c380 100644 >> --- a/doc/lispref/internals.texi >> +++ b/doc/lispref/internals.texi >> @@ -1031,10 +1031,13 @@ Writing Emacs Primitives >> @file{lisp.h} contains the definitions for some important macros and >> functions. >> >> - If you define a function which is side-effect free, update the code >> -in @file{byte-opt.el} that binds @code{side-effect-free-fns} and >> -@code{side-effect-and-error-free-fns} so that the compiler optimizer >> -knows about it. >> + If you define a function which is side-effect free, give it a >> +non-@code{nil} @code{side-effect-free} declaration (@pxref{Declare >> +Form}) or function property (@pxref{Symbol Plists}) so that the >> +compiler optimizer knows about it. Alternatively, you can update the >> +code in @file{byte-opt.el} that binds @code{side-effect-free-fns} and >> +@code{side-effect-and-error-free-fns}, but this approach is less >> +modular and no longer encouraged. > > This should have a @cindex entry for "side-effect-free" and other > symbols referenced here. Bonus points for adding some minimal > explanation what does "side-effect free function" mean. > > Thanks. Isn't this in direct contradiction with (info "(elisp) Standard Properties") which states that side-effect-free should not be set?: ‘side-effect-free’ A non-‘nil’ value indicates that the named function is free of side-effects, for determining function safety (*note Function Safety::) as well as for byte compiler optimizations. Do not set it. Thanks, Alex ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-10 14:16 ` Alex Branham @ 2019-04-10 14:34 ` Basil L. Contovounesios 2019-04-10 15:01 ` Drew Adams 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-10 14:34 UTC (permalink / raw) To: Alex Branham; +Cc: Eli Zaretskii, monnier, emacs-devel Alex Branham <branham@utexas.edu> writes: > On Wed 10 Apr 2019 at 05:32, Eli Zaretskii <eliz@gnu.org> wrote: > >>> From: "Basil L. Contovounesios" <contovob@tcd.ie> >>> Date: Tue, 09 Apr 2019 21:12:49 +0100 >>> Cc: emacs-devel@gnu.org >>> >>> diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi >>> index 8ebe47d9ad..826818c380 100644 >>> --- a/doc/lispref/internals.texi >>> +++ b/doc/lispref/internals.texi >>> @@ -1031,10 +1031,13 @@ Writing Emacs Primitives >>> @file{lisp.h} contains the definitions for some important macros and >>> functions. >>> >>> - If you define a function which is side-effect free, update the code >>> -in @file{byte-opt.el} that binds @code{side-effect-free-fns} and >>> -@code{side-effect-and-error-free-fns} so that the compiler optimizer >>> -knows about it. >>> + If you define a function which is side-effect free, give it a >>> +non-@code{nil} @code{side-effect-free} declaration (@pxref{Declare >>> +Form}) or function property (@pxref{Symbol Plists}) so that the >>> +compiler optimizer knows about it. Alternatively, you can update the >>> +code in @file{byte-opt.el} that binds @code{side-effect-free-fns} and >>> +@code{side-effect-and-error-free-fns}, but this approach is less >>> +modular and no longer encouraged. >> >> This should have a @cindex entry for "side-effect-free" and other >> symbols referenced here. Bonus points for adding some minimal >> explanation what does "side-effect free function" mean. >> >> Thanks. > > Isn't this in direct contradiction with (info "(elisp) Standard > Properties") which states that side-effect-free should not be set?: > > ‘side-effect-free’ > A non-‘nil’ value indicates that the named function is free of > side-effects, for determining function safety (*note Function > Safety::) as well as for byte compiler optimizations. Do not set > it. No, because the audience of (info "(elisp) Standard Properties") is the average Elisp author, whereas the audience of (info "(elisp) Writing Emacs Primitives") is the average contributor to Emacs core. Currently, the side-effect-free property seems to be intended as an internal one, since it makes some pretty strong guarantees, and is described only in the commentary and code of byte-opt.el. This is my interpretation of the "Do not set it" wording. Unless someone beats me to it, I intend to try to clarify this in the next version of my patch. Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2019-04-10 14:34 ` Basil L. Contovounesios @ 2019-04-10 15:01 ` Drew Adams 2019-04-10 15:45 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2019-04-10 15:01 UTC (permalink / raw) To: Basil L. Contovounesios, Alex Branham; +Cc: Eli Zaretskii, monnier, emacs-devel > > Isn't this in direct contradiction with (info "(elisp) Standard > > Properties") which states that side-effect-free should not be set?: > > > > ‘side-effect-free’ > > A non-‘nil’ value indicates that the named function is free of > > side-effects, for determining function safety (*note Function > > Safety::) as well as for byte compiler optimizations. Do not set > > it. > > No, because the audience of (info "(elisp) Standard Properties") is the > average Elisp author, whereas the audience of (info "(elisp) Writing > Emacs Primitives") is the average contributor to Emacs core. > > Currently, the side-effect-free property seems to be intended as an > internal one, since it makes some pretty strong guarantees, and is > described only in the commentary and code of byte-opt.el. This is my > interpretation of the "Do not set it" wording. > > Unless someone beats me to it, I intend to try to clarify this in the > next version of my patch. Caveat: I haven't been following this thread. 1. I don't see why anyone would say that some node of the Elisp manual is intended for "the average contributor to Emacs core" and _not_ for "the average Elisp author". The Elisp manual is (should be) for all Emacs users, even users who may never write an Elisp sexp knowingly. If you want to publish "internal" guidelines that apply only for code that we distribute as part of GNU Emacs then please do so elsewhere (e.g. in some dev readme or in code comments). I don't think that should be part of the Elisp manual. 2. On the other hand, it would be helpful if the doc for `side-effect-free' said something about _why_ it tells us not to set this. Imperatives like "Do not do X", with no other info (background) do not help users understand. They can sometimes be worse than no admonition. Our doc should help users understand, not just tell us not to do this or that. Could someone please add a sentence giving some idea about why it is not good to set this? If info about this "standard symbol" property is not something that you want to disclose to users then perhaps this property should not be mentioned in the manual. IMO that would probably be a bad, not a good thing, but if it's documented, and if we say something like "Don't set it" then we owe it to users (ourselves) to say why. Just one opinion (well, two opinions by one opinioner). ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-10 15:01 ` Drew Adams @ 2019-04-10 15:45 ` Basil L. Contovounesios 2019-04-10 16:04 ` Eli Zaretskii 2019-04-17 17:56 ` Basil L. Contovounesios 0 siblings, 2 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-10 15:45 UTC (permalink / raw) To: Drew Adams; +Cc: Eli Zaretskii, Alex Branham, monnier, emacs-devel Drew Adams <drew.adams@oracle.com> writes: >> > Isn't this in direct contradiction with (info "(elisp) Standard >> > Properties") which states that side-effect-free should not be set?: >> > >> > ‘side-effect-free’ >> > A non-‘nil’ value indicates that the named function is free of >> > side-effects, for determining function safety (*note Function >> > Safety::) as well as for byte compiler optimizations. Do not set >> > it. >> >> No, because the audience of (info "(elisp) Standard Properties") is the >> average Elisp author, whereas the audience of (info "(elisp) Writing >> Emacs Primitives") is the average contributor to Emacs core. >> >> Currently, the side-effect-free property seems to be intended as an >> internal one, since it makes some pretty strong guarantees, and is >> described only in the commentary and code of byte-opt.el. This is my >> interpretation of the "Do not set it" wording. >> >> Unless someone beats me to it, I intend to try to clarify this in the >> next version of my patch. > > Caveat: I haven't been following this thread. > > 1. I don't see why anyone would say that some node > of the Elisp manual is intended for "the average > contributor to Emacs core" and _not_ for "the average > Elisp author". Because one is a main chapter node listing standard symbol properties that one is likely to encounter, whereas the other is an appendix documenting Emacs internals. The wording of my last message may not be 100% accurate or to your liking, but it was only intended as a summary to illustrate a point. > The Elisp manual is (should be) for all Emacs users, > even users who may never write an Elisp sexp knowingly. It is and will continue to be following my next patch, if not more so. > If you want to publish "internal" guidelines that apply > only for code that we distribute as part of GNU Emacs > then please do so elsewhere (e.g. in some dev readme or > in code comments). I don't think that should be part > of the Elisp manual. There is a balance to be reached between what is and isn't documented in the manual. The Elisp manual currently documents various internals which are very useful for someone interested in hacking on Emacs like myself. It would be a shame and disservice to remove this existing documentation or avoid updating and expanding it. The 'pure' and 'side-effect-free' properties have been prominently advertised for quite a while, and are central to the operations of the compiler optimiser. The former, in particular, is frequently used by third-party Elisp packages. I think they neither qualify as too internal to document properly, nor should have their existing documentation removed. It's just that their documentation can be reworded, expanded, and restructured in a more useful way, so that it is clearer when and how to use them; that is what Eli's review was about, and what led to Alex's question. > 2. On the other hand, it would be helpful if the doc > for `side-effect-free' said something about _why_ it > tells us not to set this. > > Imperatives like "Do not do X", with no other info > (background) do not help users understand. They can > sometimes be worse than no admonition. Our doc should > help users understand, not just tell us not to do this > or that. Could someone please add a sentence giving > some idea about why it is not good to set this? > > If info about this "standard symbol" property is not > something that you want to disclose to users then > perhaps this property should not be mentioned in the > manual. IMO that would probably be a bad, not a good > thing, but if it's documented, and if we say something > like "Don't set it" then we owe it to users (ourselves) > to say why. That is exactly what I meant when I said: >> Unless someone beats me to it, I intend to try to clarify this in the >> next version of my patch. Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-10 15:45 ` Basil L. Contovounesios @ 2019-04-10 16:04 ` Eli Zaretskii 2019-04-17 17:56 ` Basil L. Contovounesios 1 sibling, 0 replies; 100+ messages in thread From: Eli Zaretskii @ 2019-04-10 16:04 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: branham, monnier, drew.adams, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Date: Wed, 10 Apr 2019 16:45:00 +0100 > Cc: Eli Zaretskii <eliz@gnu.org>, Alex Branham <branham@utexas.edu>, > monnier@iro.umontreal.ca, emacs-devel@gnu.org > > That is exactly what I meant when I said: > > >> Unless someone beats me to it, I intend to try to clarify this in the > >> next version of my patch. Indeed, it's pointless to argue about text that wasn't written yet. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-10 15:45 ` Basil L. Contovounesios 2019-04-10 16:04 ` Eli Zaretskii @ 2019-04-17 17:56 ` Basil L. Contovounesios 2019-04-17 18:11 ` Stefan Monnier ` (2 more replies) 1 sibling, 3 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-17 17:56 UTC (permalink / raw) To: Drew Adams; +Cc: Eli Zaretskii, Alex Branham, monnier, emacs-devel [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #1: 0001-Improve-pure-and-side-effect-free-docs.patch --] [-- Type: text/x-diff, Size: 6161 bytes --] From cc5646ae517f0792e5acd721f4f2f6dec7a602b3 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Wed, 17 Apr 2019 16:34:47 +0100 Subject: [PATCH 1/2] Improve pure and side-effect-free docs For discussion, see thread starting at: https://lists.gnu.org/archive/html/emacs-devel/2019-04/msg00316.html * doc/lispref/customize.texi (Composite Types): Do not overspecify :match-alternatives predicates. * doc/lispref/functions.texi (What Is a Function): Define what a pure function is. * doc/lispref/internals.texi (Writing Emacs Primitives): Describe currently preferred approach to marking primitives as pure and side-effect-free. * doc/lispref/macros.texi (Eval During Expansion): Index under 'side effect'. * doc/lispref/symbols.texi (Standard Properties): Expand description of pure and side-effect-free properties. --- doc/lispref/customize.texi | 8 ++++---- doc/lispref/functions.texi | 5 +++++ doc/lispref/internals.texi | 14 ++++++++++---- doc/lispref/macros.texi | 1 + doc/lispref/symbols.texi | 15 +++++++++++---- 5 files changed, 31 insertions(+), 12 deletions(-) diff --git a/doc/lispref/customize.texi b/doc/lispref/customize.texi index f71dedfd8b..02eefe0f58 100644 --- a/doc/lispref/customize.texi +++ b/doc/lispref/customize.texi @@ -950,10 +950,10 @@ Composite Types @itemize @bullet @item -A predicate---that is, a function of one argument that has no side -effects, and returns either @code{nil} or non-@code{nil} according to -the argument. Using a predicate in the list says that objects for which -the predicate returns non-@code{nil} are acceptable. +A predicate---that is, a function of one argument that returns either +@code{nil} or non-@code{nil} according to the argument. Using a +predicate in the list says that objects for which the predicate +returns non-@code{nil} are acceptable. @item A quoted constant---that is, @code{'@var{object}}. This sort of element diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 222f863c98..0f9c977268 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -38,11 +38,16 @@ What Is a Function @cindex return value @cindex value of function @cindex argument +@cindex side effect +@cindex pure function In a general sense, a function is a rule for carrying out a computation given input values called @dfn{arguments}. The result of the computation is called the @dfn{value} or @dfn{return value} of the function. The computation can also have side effects, such as lasting changes in the values of variables or the contents of data structures. +A @dfn{pure function} is a function which, in addition to having no +side effects, always returns the same value for the same combination +of arguments, regardless of external factors such as machine type. In most computer languages, every function has a name. But in Lisp, a function in the strictest sense has no name: it is an object which diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index 8ebe47d9ad..9947e37ca1 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1031,10 +1031,16 @@ Writing Emacs Primitives @file{lisp.h} contains the definitions for some important macros and functions. - If you define a function which is side-effect free, update the code -in @file{byte-opt.el} that binds @code{side-effect-free-fns} and -@code{side-effect-and-error-free-fns} so that the compiler optimizer -knows about it. +@cindex @code{side-effect-free} +@cindex @code{pure} +@vindex side-effect-free-fns +@vindex side-effect-and-error-free-fns + If you define a function which is side-effect free and/or pure, give +it a non-@code{nil} @code{side-effect-free} and/or @code{pure} +property, respectively (@pxref{Standard Properties}). Alternatively, +you can update the code in @file{byte-opt.el} that binds +@code{side-effect-free-fns} and @code{side-effect-and-error-free-fns}, +but this approach is less modular and no longer encouraged. @node Writing Dynamic Modules @section Writing Dynamically-Loaded Modules diff --git a/doc/lispref/macros.texi b/doc/lispref/macros.texi index a422ba9750..bcee5b1078 100644 --- a/doc/lispref/macros.texi +++ b/doc/lispref/macros.texi @@ -523,6 +523,7 @@ Eval During Expansion @node Repeated Expansion @subsection How Many Times is the Macro Expanded? +@cindex side effect Occasionally problems result from the fact that a macro call is expanded each time it is evaluated in an interpreted function, but is diff --git a/doc/lispref/symbols.texi b/doc/lispref/symbols.texi index a214a2d3fd..e49c500701 100644 --- a/doc/lispref/symbols.texi +++ b/doc/lispref/symbols.texi @@ -558,9 +558,12 @@ Standard Properties modes. @xref{Setting Hooks}. @item pure +@cindex @code{pure} If the value is non-@code{nil}, the named function is considered to be -side-effect free. Calls with constant arguments can be evaluated at -compile time. This may shift run time errors to compile time. +side-effect free and pure (@pxref{What Is a Function}). Calls with +constant arguments can be evaluated at compile time. This may shift +run time errors to compile time. Not to be confused with pure storage +(@pxref{Pure Storage}). @item risky-local-variable If the value is non-@code{nil}, the named variable is considered risky @@ -579,9 +582,13 @@ Standard Properties for the named variable. @xref{File Local Variables}. @item side-effect-free +@cindex @code{side-effect-free} A non-@code{nil} value indicates that the named function is free of -side-effects, for determining function safety (@pxref{Function -Safety}) as well as for byte compiler optimizations. Do not set it. +side effects (@pxref{What Is a Function}), so the byte compiler may +ignore calls whose value is unused. If the property's value is +@code{error-free}, the byte compiler may even delete such unused +calls. In addition to byte compiler optimizations, this property is +also used for determining function safety (@pxref{Function Safety}). @item variable-documentation If non-@code{nil}, this specifies the named variable's documentation -- 2.20.1 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0002-Move-side-effect-free-from-unsafep.el-to-subr.el.patch --] [-- Type: text/x-diff, Size: 3828 bytes --] From e0e72e306400fb129379d1af3a8fbd3fd643fa6c Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Wed, 17 Apr 2019 17:35:12 +0100 Subject: [PATCH 2/2] Move side-effect-free from unsafep.el to subr.el * lisp/emacs-lisp/unsafep.el: Move side-effect-free property setting from here... * lisp/subr.el: ...to here, as function declarations for modularity. --- lisp/emacs-lisp/unsafep.el | 5 ----- lisp/subr.el | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/lisp/emacs-lisp/unsafep.el b/lisp/emacs-lisp/unsafep.el index d20b751d88..1a2f1f31b1 100644 --- a/lisp/emacs-lisp/unsafep.el +++ b/lisp/emacs-lisp/unsafep.el @@ -92,11 +92,6 @@ unsafep-vars in the parse.") (put 'unsafep-vars 'risky-local-variable t) -;;Side-effect-free functions from subr.el -(dolist (x '(assoc-default butlast last match-string - match-string-no-properties member-ignore-case remove remq)) - (put x 'side-effect-free t)) - ;;Other safe functions (dolist (x '(;;Special forms and catch if or prog1 prog2 progn while unwind-protect diff --git a/lisp/subr.el b/lisp/subr.el index bf3716bbd3..f68f9dd419 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -580,6 +580,7 @@ last If LIST is nil, return nil. If N is non-nil, return the Nth-to-last link of LIST. If N is bigger than the length of LIST, return LIST." + (declare (side-effect-free t)) (if n (and (>= n 0) (let ((m (safe-length list))) @@ -591,6 +592,7 @@ butlast "Return a copy of LIST with the last N elements removed. If N is omitted or nil, the last element is removed from the copy." + (declare (side-effect-free t)) (if (and n (<= n 0)) list (nbutlast (copy-sequence list) n))) @@ -726,6 +728,7 @@ assoc-default If no element matches, the value is nil. If TEST is omitted or nil, `equal' is used." + (declare (side-effect-free t)) (let (found (tail alist) value) (while (and tail (not found)) (let ((elt (car tail))) @@ -739,6 +742,7 @@ member-ignore-case ELT must be a string. Upper-case and lower-case letters are treated as equal. Unibyte strings are converted to multibyte for comparison. Non-strings in LIST are ignored." + (declare (side-effect-free t)) (while (and list (not (and (stringp (car list)) (eq t (compare-strings elt 0 nil (car list) 0 nil t))))) @@ -822,6 +826,7 @@ alist-get (defun remove (elt seq) "Return a copy of SEQ with all occurrences of ELT removed. SEQ must be a list, vector, or string. The comparison is done with `equal'." + (declare (side-effect-free t)) (if (nlistp seq) ;; If SEQ isn't a list, there's no need to copy SEQ because ;; `delete' will return a new object. @@ -832,6 +837,7 @@ remq "Return LIST with all occurrences of ELT removed. The comparison is done with `eq'. Contrary to `delq', this does not use side-effects, and the argument LIST is not modified." + (declare (side-effect-free t)) (while (and (eq elt (car list)) (setq list (cdr list)))) (if (memq elt list) (delq elt (copy-sequence list)) @@ -3898,6 +3904,7 @@ match-string STRING should be given if the last search was by `string-match' on STRING. If STRING is nil, the current buffer should be the same buffer the search/match was performed in." + (declare (side-effect-free t)) (if (match-beginning num) (if string (substring string (match-beginning num) (match-end num)) @@ -3911,6 +3918,7 @@ match-string-no-properties STRING should be given if the last search was by `string-match' on STRING. If STRING is nil, the current buffer should be the same buffer the search/match was performed in." + (declare (side-effect-free t)) (if (match-beginning num) (if string (substring-no-properties string (match-beginning num) -- 2.20.1 [-- Attachment #3: Type: text/plain, Size: 1530 bytes --] "Basil L. Contovounesios" <contovob@tcd.ie> writes: >> Isn't this in direct contradiction with (info "(elisp) Standard >> Properties") which states that side-effect-free should not be set?: >> >> ‘side-effect-free’ >> A non-‘nil’ value indicates that the named function is free of >> side-effects, for determining function safety (*note Function >> Safety::) as well as for byte compiler optimizations. Do not set >> it. > > No, because the audience of (info "(elisp) Standard Properties") is the > average Elisp author, whereas the audience of (info "(elisp) Writing > Emacs Primitives") is the average contributor to Emacs core. > > Currently, the side-effect-free property seems to be intended as an > internal one, since it makes some pretty strong guarantees, and is > described only in the commentary and code of byte-opt.el. This is my > interpretation of the "Do not set it" wording. Upon further reading of byte-opt.el and byte-run.el and inspection of the history of this documentation, I see nothing internal about the side-effect-free property. In other words, this seems to be a near-identical case of bug#13823. > Unless someone beats me to it, I intend to try to clarify this in the > next version of my patch. I attach two patches. The first updates the Elisp manual as discussed, and the second moves all side-effect-free property setting from unsafep.el to subr.el, as declarations of the relevant functions. WDYT? Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-17 17:56 ` Basil L. Contovounesios @ 2019-04-17 18:11 ` Stefan Monnier 2019-04-21 21:42 ` Basil L. Contovounesios 2019-04-17 18:55 ` Drew Adams 2019-04-18 14:37 ` Eli Zaretskii 2 siblings, 1 reply; 100+ messages in thread From: Stefan Monnier @ 2019-04-17 18:11 UTC (permalink / raw) To: emacs-devel I'll let other people discuss the doc, but regarding the code: > * lisp/emacs-lisp/unsafep.el: Move side-effect-free property setting > from here... > * lisp/subr.el: ...to here, as function declarations for modularity. LGTM. Stefan ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-17 18:11 ` Stefan Monnier @ 2019-04-21 21:42 ` Basil L. Contovounesios 0 siblings, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-21 21:42 UTC (permalink / raw) To: Stefan Monnier; +Cc: emacs-devel Stefan Monnier <monnier@iro.umontreal.ca> writes: > I'll let other people discuss the doc, but regarding the code: > >> * lisp/emacs-lisp/unsafep.el: Move side-effect-free property setting >> from here... >> * lisp/subr.el: ...to here, as function declarations for modularity. > > LGTM. Thanks, pushed to master earlier[1]. [1: 3a618e5f89]: Move side-effect-free from unsafep.el to subr.el 2019-04-21 19:04:13 +0100 https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=3a618e5f89c86bc96925b06647fb33568c8fa2c9 -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2019-04-17 17:56 ` Basil L. Contovounesios 2019-04-17 18:11 ` Stefan Monnier @ 2019-04-17 18:55 ` Drew Adams 2019-04-21 21:24 ` Basil L. Contovounesios 2019-04-18 14:37 ` Eli Zaretskii 2 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2019-04-17 18:55 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: Eli Zaretskii, Alex Branham, monnier, emacs-devel > this seems to be a near-identical case of bug#13823. Indeed it does. > I attach two patches. The first updates the Elisp manual as discussed, > and the second moves all side-effect-free property setting from > unsafep.el to subr.el, as declarations of the relevant functions. WDYT? Thanks for working on this. Some minor comments, in case they help (if not, ignore): 1. Wrt this proposed addition: +A @dfn{pure function} is a function which, in addition to having no +side effects, always returns the same value for the same combination +of arguments, regardless of external factors such as machine type. You might want to add something like this at the end: ", current time/date, or state of the system or Emacs session." "Machine type" is not the first thing I'd think of in this regard, and it doesn't emphasize current state in the context of dynamic changes. The result does not depend on any state. 2. It's usually better to just write "or" instead of "and/or". 3. I'd say "not encouraged" instead of "no longer encouraged". There's no need here to suggest that it was encouraged in the past. 4. "side-effect free and pure" should just be "pure", given that the xref'd node will now say that "pure" includes "side-effect free". 5. "ignore calls whose value is unused" is maybe clearer as "ignore a call whose value is unused" or "ignore calls whose values are unused". (I prefer the former.) 6. I'd use "can" instead of "may" for things like "the byte compiler may...". (Not very important, though.) It's more about possibility than permission. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-17 18:55 ` Drew Adams @ 2019-04-21 21:24 ` Basil L. Contovounesios 2019-04-22 0:03 ` Drew Adams 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-21 21:24 UTC (permalink / raw) To: Drew Adams; +Cc: Eli Zaretskii, Alex Branham, monnier, emacs-devel Drew Adams <drew.adams@oracle.com> writes: >> this seems to be a near-identical case of bug#13823. > > Indeed it does. > >> I attach two patches. The first updates the Elisp manual as discussed, >> and the second moves all side-effect-free property setting from >> unsafep.el to subr.el, as declarations of the relevant functions. WDYT? > > Thanks for working on this. Some minor comments, in case they help (if not, ignore): > > 1. Wrt this proposed addition: > > +A @dfn{pure function} is a function which, in addition to having no > +side effects, always returns the same value for the same combination > +of arguments, regardless of external factors such as machine type. > > You might want to add something like this at the end: > > ", current time/date, or state of the system or > Emacs session." > > "Machine type" is not the first thing I'd think of in this > regard, and it doesn't emphasize current state in the context > of dynamic changes. The result does not depend on any state. Right. Would "machine type or system state" suffice? I think this covers time/date, etc. > 2. It's usually better to just write "or" instead of "and/or". Done. > 3. I'd say "not encouraged" instead of "no longer encouraged". > There's no need here to suggest that it was encouraged in the > past. This sentence will be removed altogether. > 4. "side-effect free and pure" should just be "pure", given > that the xref'd node will now say that "pure" includes > "side-effect free". Done. > 5. "ignore calls whose value is unused" is maybe clearer as > "ignore a call whose value is unused" or "ignore calls whose > values are unused". (I prefer the former.) Changed to the former. > 6. I'd use "can" instead of "may" for things like "the byte > compiler may...". (Not very important, though.) It's more > about possibility than permission. Yes, but may/might are about both possibility and permission, and can is about both possibility and ability. The former combination seems more appropriate to me in this case, but I'm not opposed to changing it. Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2019-04-21 21:24 ` Basil L. Contovounesios @ 2019-04-22 0:03 ` Drew Adams 2019-04-22 1:12 ` Michael Heerdegen 0 siblings, 1 reply; 100+ messages in thread From: Drew Adams @ 2019-04-22 0:03 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: Eli Zaretskii, Alex Branham, monnier, emacs-devel > > 1. Wrt this proposed addition: > > > > +A @dfn{pure function} is a function which, in addition to having no > > +side effects, always returns the same value for the same combination > > +of arguments, regardless of external factors such as machine type. > > > > You might want to add something like this at the end: > > > > ", current time/date, or state of the system or > > Emacs session." > > > > "Machine type" is not the first thing I'd think of in this > > regard, and it doesn't emphasize current state in the context > > of dynamic changes. The result does not depend on any state. > > Right. Would "machine type or system state" suffice? I think this > covers time/date, etc. Anything that mentions state (which could be _any_ state available to Emacs - not just system state, if "system" means OS) is better than no mention of state. Emacs could read a sensor or get state from a process report or... ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-22 0:03 ` Drew Adams @ 2019-04-22 1:12 ` Michael Heerdegen 2019-04-22 9:39 ` Drew Adams 0 siblings, 1 reply; 100+ messages in thread From: Michael Heerdegen @ 2019-04-22 1:12 UTC (permalink / raw) To: Drew Adams Cc: Basil L. Contovounesios, Eli Zaretskii, Alex Branham, monnier, emacs-devel Drew Adams <drew.adams@oracle.com> writes: > Anything that mentions state (which could be _any_ state > available to Emacs - not just system state I think the most important thing is the state of Emacs: variable bindings (free variables in the function body!), current buffer, value of point, etc. Since only stuff matters that Emacs has access to (and this access is always via functions and variables), one could even count everything that matters as part of Emacs' state. What I want to say that mentioning too much external stuff as examples is more confusing than helpful. Michael. ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2019-04-22 1:12 ` Michael Heerdegen @ 2019-04-22 9:39 ` Drew Adams 0 siblings, 0 replies; 100+ messages in thread From: Drew Adams @ 2019-04-22 9:39 UTC (permalink / raw) To: Michael Heerdegen Cc: Basil L. Contovounesios, Eli Zaretskii, Alex Branham, monnier, emacs-devel > > Anything that mentions state (which could be _any_ state > > available to Emacs - not just system state ^^^^^^^^^^^^^^^^^^ > I think the most important thing is the state of Emacs: variable > bindings (free variables in the function body!), current buffer, value > of point, etc. Since only stuff matters that Emacs has access to (and > this access is always via functions and variables), one could even count > everything that matters as part of Emacs' state. > > What I want to say that mentioning too much external stuff as examples > is more confusing than helpful. I agree. That's most important, by far. (OTOH, if we're trying to give a general definition of pure then any state at all counts. Still, it's only possible for a computable function to take into account state that is available to it...) ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-17 17:56 ` Basil L. Contovounesios 2019-04-17 18:11 ` Stefan Monnier 2019-04-17 18:55 ` Drew Adams @ 2019-04-18 14:37 ` Eli Zaretskii 2019-04-21 18:30 ` Basil L. Contovounesios 2 siblings, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2019-04-18 14:37 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: branham, monnier, drew.adams, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: Alex Branham <branham@utexas.edu>, Eli Zaretskii <eliz@gnu.org>, monnier@iro.umontreal.ca, emacs-devel@gnu.org > Date: Wed, 17 Apr 2019 18:56:40 +0100 > > --- a/doc/lispref/functions.texi > +++ b/doc/lispref/functions.texi > @@ -38,11 +38,16 @@ What Is a Function > @cindex return value > @cindex value of function > @cindex argument > +@cindex side effect > +@cindex pure function > In a general sense, a function is a rule for carrying out a > computation given input values called @dfn{arguments}. The result of > the computation is called the @dfn{value} or @dfn{return value} of the > function. The computation can also have side effects, such as lasting > changes in the values of variables or the contents of data structures. > +A @dfn{pure function} is a function which, in addition to having no > +side effects, always returns the same value for the same combination > +of arguments, regardless of external factors such as machine type. The index entry "side effect" here means that this term is explained. But the text doesn't live up to that promise, it just gives an example. Can we add a more general explanation? > +@cindex @code{side-effect-free} > +@cindex @code{pure} > +@vindex side-effect-free-fns > +@vindex side-effect-and-error-free-fns Some of these index entries are redundant, because they start with the same string and point to the same place in the manual. Also, you already have an index entry "pure" elsewhere. If you think there should be another one that points here, please qualify one or both, so that the entries will be different. having identical index entries gets in the way when you type "i foo TAB" and are presented with completion candidates "foo" and "foo<1>", with no way of telling which one do you want. > + If you define a function which is side-effect free and/or pure, give > +it a non-@code{nil} @code{side-effect-free} and/or @code{pure} > +property, respectively (@pxref{Standard Properties}). Alternatively, > +you can update the code in @file{byte-opt.el} that binds > +@code{side-effect-free-fns} and @code{side-effect-and-error-free-fns}, > +but this approach is less modular and no longer encouraged. I don't think we need to tell in the manual when to modify the sources, so I think the last sentence should be removed. > --- a/doc/lispref/macros.texi > +++ b/doc/lispref/macros.texi > @@ -523,6 +523,7 @@ Eval During Expansion > > @node Repeated Expansion > @subsection How Many Times is the Macro Expanded? > +@cindex side effect Once again, a duplicate index entry without qualifications. > --- a/doc/lispref/symbols.texi > +++ b/doc/lispref/symbols.texi > @@ -558,9 +558,12 @@ Standard Properties > modes. @xref{Setting Hooks}. > > @item pure > +@cindex @code{pure} Likewise. > @@ -579,9 +582,13 @@ Standard Properties > for the named variable. @xref{File Local Variables}. > > @item side-effect-free > +@cindex @code{side-effect-free} And again. Thanks. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-18 14:37 ` Eli Zaretskii @ 2019-04-21 18:30 ` Basil L. Contovounesios 2019-04-21 19:39 ` Eli Zaretskii 2019-04-21 19:41 ` Eli Zaretskii 0 siblings, 2 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-21 18:30 UTC (permalink / raw) To: Eli Zaretskii; +Cc: branham, monnier, drew.adams, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Cc: Alex Branham <branham@utexas.edu>, Eli Zaretskii <eliz@gnu.org>, >> monnier@iro.umontreal.ca, emacs-devel@gnu.org >> Date: Wed, 17 Apr 2019 18:56:40 +0100 >> >> --- a/doc/lispref/functions.texi >> +++ b/doc/lispref/functions.texi >> @@ -38,11 +38,16 @@ What Is a Function >> @cindex return value >> @cindex value of function >> @cindex argument >> +@cindex side effect >> +@cindex pure function >> In a general sense, a function is a rule for carrying out a >> computation given input values called @dfn{arguments}. The result of >> the computation is called the @dfn{value} or @dfn{return value} of the >> function. The computation can also have side effects, such as lasting >> changes in the values of variables or the contents of data structures. >> +A @dfn{pure function} is a function which, in addition to having no >> +side effects, always returns the same value for the same combination >> +of arguments, regardless of external factors such as machine type. > > The index entry "side effect" here means that this term is explained. > But the text doesn't live up to that promise, it just gives an > example. Can we add a more general explanation? Sorry, I think I misunderstood how these indices work, and was using them to label relevant parts of the documentation, which should probably be done with cross-references instead. The phrase "side effect" is actually defined under (info "(elisp) Intro Eval"), so I will remove this index entry. Is the definition there satisfactory and general enough? If not, what is it missing? Either way, can the explanation of side effects in (info "(elisp) What Is a Function") remain as it is, perhaps with a cross-reference to the definition in (info "(elisp) Intro Eval")? If not, what is it missing? >> +@cindex @code{side-effect-free} >> +@cindex @code{pure} >> +@vindex side-effect-free-fns >> +@vindex side-effect-and-error-free-fns > > Some of these index entries are redundant, because they start with the > same string and point to the same place in the manual. > > Also, you already have an index entry "pure" elsewhere. If you think > there should be another one that points here, please qualify one or > both, so that the entries will be different. having identical index > entries gets in the way when you type "i foo TAB" and are > presented with completion candidates "foo" and "foo<1>", with no way > of telling which one do you want. I will remove all four of these index entries. The first two because the definitions of these properties are cross-referenced in the following paragraph, and the latter two because the sentence referencing them is to be removed. >> + If you define a function which is side-effect free and/or pure, give >> +it a non-@code{nil} @code{side-effect-free} and/or @code{pure} >> +property, respectively (@pxref{Standard Properties}). Alternatively, >> +you can update the code in @file{byte-opt.el} that binds >> +@code{side-effect-free-fns} and @code{side-effect-and-error-free-fns}, >> +but this approach is less modular and no longer encouraged. > > I don't think we need to tell in the manual when to modify the > sources, so I think the last sentence should be removed. OK. >> --- a/doc/lispref/macros.texi >> +++ b/doc/lispref/macros.texi >> @@ -523,6 +523,7 @@ Eval During Expansion >> >> @node Repeated Expansion >> @subsection How Many Times is the Macro Expanded? >> +@cindex side effect > > Once again, a duplicate index entry without qualifications. Will remove. >> --- a/doc/lispref/symbols.texi >> +++ b/doc/lispref/symbols.texi >> @@ -558,9 +558,12 @@ Standard Properties >> modes. @xref{Setting Hooks}. >> >> @item pure >> +@cindex @code{pure} > > Likewise. This is where the property is defined. If property names are not usually indexed, I can remove this index entry. Otherwise, is the following qualification OK? @cindex @code{pure} property >> @@ -579,9 +582,13 @@ Standard Properties >> for the named variable. @xref{File Local Variables}. >> >> @item side-effect-free >> +@cindex @code{side-effect-free} > > And again. Mutatis mutandis. Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-21 18:30 ` Basil L. Contovounesios @ 2019-04-21 19:39 ` Eli Zaretskii 2019-04-21 21:37 ` Basil L. Contovounesios 2019-04-21 19:41 ` Eli Zaretskii 1 sibling, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2019-04-21 19:39 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: branham, monnier, drew.adams, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> > Date: Sun, 21 Apr 2019 19:30:53 +0100 > > > The index entry "side effect" here means that this term is explained. > > But the text doesn't live up to that promise, it just gives an > > example. Can we add a more general explanation? > > Sorry, I think I misunderstood how these indices work, and was using > them to label relevant parts of the documentation, which should probably > be done with cross-references instead. If text just mentions a term, but doesn't intend to explain it, it should indeed provide a cross-reference to where the term is explained/defined. > The phrase "side effect" is actually defined under (info "(elisp) Intro > Eval"), so I will remove this index entry. Is the definition there > satisfactory and general enough? Yes, I think so. > Either way, can the explanation of side effects in (info "(elisp) What > Is a Function") remain as it is, perhaps with a cross-reference to the > definition in (info "(elisp) Intro Eval")? Yes, with a cross-reference. > >> --- a/doc/lispref/symbols.texi > >> +++ b/doc/lispref/symbols.texi > >> @@ -558,9 +558,12 @@ Standard Properties > >> modes. @xref{Setting Hooks}. > >> > >> @item pure > >> +@cindex @code{pure} > > > > Likewise. > > This is where the property is defined. If property names are not > usually indexed, I can remove this index entry. Otherwise, is the > following qualification OK? > > @cindex @code{pure} property Yes, this is fine. Thanks. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-21 19:39 ` Eli Zaretskii @ 2019-04-21 21:37 ` Basil L. Contovounesios 2019-04-22 0:06 ` Drew Adams 2019-04-22 7:49 ` Eli Zaretskii 0 siblings, 2 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-21 21:37 UTC (permalink / raw) To: Eli Zaretskii; +Cc: branham, monnier, drew.adams, emacs-devel [-- Attachment #1: Type: text/plain, Size: 36 bytes --] How's the following updated patch? [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-Improve-pure-and-side-effect-free-docs.patch --] [-- Type: text/x-diff, Size: 5953 bytes --] From 7bbcfcfc201d8a924b9f19abcc9307001b79ec86 Mon Sep 17 00:00:00 2001 From: "Basil L. Contovounesios" <contovob@tcd.ie> Date: Wed, 17 Apr 2019 16:34:47 +0100 Subject: [PATCH] Improve pure and side-effect-free docs For discussion, see thread starting at: https://lists.gnu.org/archive/html/emacs-devel/2019-04/msg00316.html * doc/lispref/customize.texi (Composite Types): Do not overspecify :match-alternatives predicates. * doc/lispref/eval.texi (Intro Eval): Anchor definition of "side effect" for cross-referencing... * doc/lispref/functions.texi (What Is a Function): ...from here. Define what a pure function is. * doc/lispref/internals.texi (Writing Emacs Primitives): Describe currently preferred approach to marking primitives as pure and side-effect-free. * doc/lispref/symbols.texi (Standard Properties): Expand description of pure and side-effect-free properties. --- doc/lispref/customize.texi | 8 ++++---- doc/lispref/eval.texi | 1 + doc/lispref/functions.texi | 7 ++++++- doc/lispref/internals.texi | 7 +++---- doc/lispref/symbols.texi | 15 +++++++++++---- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/doc/lispref/customize.texi b/doc/lispref/customize.texi index f71dedfd8b..02eefe0f58 100644 --- a/doc/lispref/customize.texi +++ b/doc/lispref/customize.texi @@ -950,10 +950,10 @@ Composite Types @itemize @bullet @item -A predicate---that is, a function of one argument that has no side -effects, and returns either @code{nil} or non-@code{nil} according to -the argument. Using a predicate in the list says that objects for which -the predicate returns non-@code{nil} are acceptable. +A predicate---that is, a function of one argument that returns either +@code{nil} or non-@code{nil} according to the argument. Using a +predicate in the list says that objects for which the predicate +returns non-@code{nil} are acceptable. @item A quoted constant---that is, @code{'@var{object}}. This sort of element diff --git a/doc/lispref/eval.texi b/doc/lispref/eval.texi index db42dfb637..39c5a1ec50 100644 --- a/doc/lispref/eval.texi +++ b/doc/lispref/eval.texi @@ -87,6 +87,7 @@ Intro Eval (@pxref{Local Variables}). @cindex side effect +@anchor{Definition of side effect} Evaluating a form may also make changes that persist; these changes are called @dfn{side effects}. An example of a form that produces a side effect is @code{(setq foo 1)}. diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 222f863c98..97f7fb9f79 100644 --- a/doc/lispref/functions.texi +++ b/doc/lispref/functions.texi @@ -38,11 +38,16 @@ What Is a Function @cindex return value @cindex value of function @cindex argument +@cindex pure function In a general sense, a function is a rule for carrying out a computation given input values called @dfn{arguments}. The result of the computation is called the @dfn{value} or @dfn{return value} of the function. The computation can also have side effects, such as lasting -changes in the values of variables or the contents of data structures. +changes in the values of variables or the contents of data structures +(@pxref{Definition of side effect}). A @dfn{pure function} is a +function which, in addition to having no side effects, always returns +the same value for the same combination of arguments, regardless of +external factors such as machine type or system state. In most computer languages, every function has a name. But in Lisp, a function in the strictest sense has no name: it is an object which diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi index fc5ce594e6..25892d4b57 100644 --- a/doc/lispref/internals.texi +++ b/doc/lispref/internals.texi @@ -1031,10 +1031,9 @@ Writing Emacs Primitives @file{lisp.h} contains the definitions for some important macros and functions. - If you define a function which is side-effect free, update the code -in @file{byte-opt.el} that binds @code{side-effect-free-fns} and -@code{side-effect-and-error-free-fns} so that the compiler optimizer -knows about it. + If you define a function which is side-effect free or pure, give it +a non-@code{nil} @code{side-effect-free} or @code{pure} property, +respectively (@pxref{Standard Properties}). @node Writing Dynamic Modules @section Writing Dynamically-Loaded Modules diff --git a/doc/lispref/symbols.texi b/doc/lispref/symbols.texi index a214a2d3fd..5d71fb39a2 100644 --- a/doc/lispref/symbols.texi +++ b/doc/lispref/symbols.texi @@ -558,9 +558,12 @@ Standard Properties modes. @xref{Setting Hooks}. @item pure +@cindex @code{pure} property If the value is non-@code{nil}, the named function is considered to be -side-effect free. Calls with constant arguments can be evaluated at -compile time. This may shift run time errors to compile time. +pure (@pxref{What Is a Function}). Calls with constant arguments can +be evaluated at compile time. This may shift run time errors to +compile time. Not to be confused with pure storage (@pxref{Pure +Storage}). @item risky-local-variable If the value is non-@code{nil}, the named variable is considered risky @@ -579,9 +582,13 @@ Standard Properties for the named variable. @xref{File Local Variables}. @item side-effect-free +@cindex @code{side-effect-free} property A non-@code{nil} value indicates that the named function is free of -side-effects, for determining function safety (@pxref{Function -Safety}) as well as for byte compiler optimizations. Do not set it. +side effects (@pxref{What Is a Function}), so the byte compiler may +ignore a call whose value is unused. If the property's value is +@code{error-free}, the byte compiler may even delete such unused +calls. In addition to byte compiler optimizations, this property is +also used for determining function safety (@pxref{Function Safety}). @item variable-documentation If non-@code{nil}, this specifies the named variable's documentation -- 2.20.1 [-- Attachment #3: Type: text/plain, Size: 20 bytes --] Thanks, -- Basil ^ permalink raw reply related [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2019-04-21 21:37 ` Basil L. Contovounesios @ 2019-04-22 0:06 ` Drew Adams 2019-04-22 7:49 ` Eli Zaretskii 1 sibling, 0 replies; 100+ messages in thread From: Drew Adams @ 2019-04-22 0:06 UTC (permalink / raw) To: Basil L. Contovounesios, Eli Zaretskii; +Cc: branham, monnier, emacs-devel > How's the following updated patch? Yes, the doc is better than before. Thx. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-21 21:37 ` Basil L. Contovounesios 2019-04-22 0:06 ` Drew Adams @ 2019-04-22 7:49 ` Eli Zaretskii 2019-04-22 12:59 ` Basil L. Contovounesios 1 sibling, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2019-04-22 7:49 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: branham, monnier, drew.adams, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> > Date: Sun, 21 Apr 2019 22:37:14 +0100 > > How's the following updated patch? LGTM, thanks. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-22 7:49 ` Eli Zaretskii @ 2019-04-22 12:59 ` Basil L. Contovounesios 2019-04-22 13:12 ` Eli Zaretskii 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-22 12:59 UTC (permalink / raw) To: Eli Zaretskii; +Cc: branham, monnier, drew.adams, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> >> Date: Sun, 21 Apr 2019 22:37:14 +0100 >> >> How's the following updated patch? > > LGTM, thanks. Thanks, pushed to master without thinking; this should be backported to emacs-26 as well, right? -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-22 12:59 ` Basil L. Contovounesios @ 2019-04-22 13:12 ` Eli Zaretskii 2019-04-22 15:19 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2019-04-22 13:12 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: branham, monnier, drew.adams, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> > Date: Mon, 22 Apr 2019 13:59:07 +0100 > > Thanks, pushed to master without thinking; this should be backported to > emacs-26 as well, right? Yep. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-22 13:12 ` Eli Zaretskii @ 2019-04-22 15:19 ` Basil L. Contovounesios 0 siblings, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-22 15:19 UTC (permalink / raw) To: Eli Zaretskii; +Cc: branham, monnier, drew.adams, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> >> Date: Mon, 22 Apr 2019 13:59:07 +0100 >> >> Thanks, pushed to master without thinking; this should be backported to >> emacs-26 as well, right? > > Yep. Thanks, done: [1: 3988e93d4b]: Backport: Improve pure and side-effect-free docs 2019-04-22 16:14:33 +0100 https://git.savannah.gnu.org/cgit/emacs.git/commit/?id=3988e93d4b0f2bf677efd9f560373dd526097609 -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-21 18:30 ` Basil L. Contovounesios 2019-04-21 19:39 ` Eli Zaretskii @ 2019-04-21 19:41 ` Eli Zaretskii 2019-04-21 21:41 ` Basil L. Contovounesios 1 sibling, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2019-04-21 19:41 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: branham, monnier, drew.adams, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> > Date: Sun, 21 Apr 2019 19:30:53 +0100 > > Sorry, I think I misunderstood how these indices work Each time you write @cindex (or @findex, or @vindex, or ...), the entry you wrote is added to the Index, and it points to the place where you wrote the above. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-21 19:41 ` Eli Zaretskii @ 2019-04-21 21:41 ` Basil L. Contovounesios 2019-04-22 6:39 ` Eli Zaretskii 0 siblings, 1 reply; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-21 21:41 UTC (permalink / raw) To: Eli Zaretskii; +Cc: branham, monnier, drew.adams, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, >> <emacs-devel@gnu.org> >> Date: Sun, 21 Apr 2019 19:30:53 +0100 >> >> Sorry, I think I misunderstood how these indices work > > Each time you write @cindex (or @findex, or @vindex, or ...), the > entry you wrote is added to the Index, and it points to the place > where you wrote the above. I understood this, but thought that every section relevant to some key should be indexed by that key, not just the section which defines it. I understand now that this is not the case. Thanks, -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-21 21:41 ` Basil L. Contovounesios @ 2019-04-22 6:39 ` Eli Zaretskii 2019-04-22 12:58 ` Basil L. Contovounesios 0 siblings, 1 reply; 100+ messages in thread From: Eli Zaretskii @ 2019-04-22 6:39 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: branham, monnier, drew.adams, emacs-devel > From: "Basil L. Contovounesios" <contovob@tcd.ie> > Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> > Date: Sun, 21 Apr 2019 22:41:39 +0100 > > > Each time you write @cindex (or @findex, or @vindex, or ...), the > > entry you wrote is added to the Index, and it points to the place > > where you wrote the above. > > I understood this, but thought that every section relevant to some key > should be indexed by that key, not just the section which defines it. > I understand now that this is not the case. This is not the case. The index entry should point to the place where the subject is described and discussed in its fullest. If there are certain specific aspects of the subject whose description is elsewhere (e.g., because it would be out of place or inappropriate as part of the main description, perhaps because some missing context), then add qualified index entries that point to those other places, as in @cindex foo -- the main index entry @cindex foo, and bar -- index entry for discussion of foo in context of bar Here are two real-life examples of the latter: @cindex display property, and point display @cindex display property, unsafe evaluation Thanks. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2019-04-22 6:39 ` Eli Zaretskii @ 2019-04-22 12:58 ` Basil L. Contovounesios 0 siblings, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2019-04-22 12:58 UTC (permalink / raw) To: Eli Zaretskii; +Cc: branham, monnier, drew.adams, emacs-devel Eli Zaretskii <eliz@gnu.org> writes: >> From: "Basil L. Contovounesios" <contovob@tcd.ie> >> Cc: <drew.adams@oracle.com>, <branham@utexas.edu>, <monnier@iro.umontreal.ca>, <emacs-devel@gnu.org> >> Date: Sun, 21 Apr 2019 22:41:39 +0100 >> >> > Each time you write @cindex (or @findex, or @vindex, or ...), the >> > entry you wrote is added to the Index, and it points to the place >> > where you wrote the above. >> >> I understood this, but thought that every section relevant to some key >> should be indexed by that key, not just the section which defines it. >> I understand now that this is not the case. > > This is not the case. The index entry should point to the place where > the subject is described and discussed in its fullest. If there are > certain specific aspects of the subject whose description is elsewhere > (e.g., because it would be out of place or inappropriate as part of > the main description, perhaps because some missing context), then add > qualified index entries that point to those other places, as in > > @cindex foo -- the main index entry > @cindex foo, and bar -- index entry for discussion of foo in context of bar > > Here are two real-life examples of the latter: > > @cindex display property, and point display > @cindex display property, unsafe evaluation Makes sense, thanks for explaining. -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* RE: Predicate for true lists 2018-07-05 22:31 ` Basil L. Contovounesios 2018-07-06 5:57 ` Eli Zaretskii @ 2018-07-06 17:00 ` Drew Adams 2018-07-06 17:20 ` Paul Eggert ` (2 more replies) 2018-07-06 17:30 ` Paul Eggert 2 siblings, 3 replies; 100+ messages in thread From: Drew Adams @ 2018-07-06 17:00 UTC (permalink / raw) To: Basil L. Contovounesios, Paul Eggert; +Cc: emacs-devel 1. Why is "proper" better than "true"? I don't argue against "proper", but what is really gained by this change in Emacs terminology? Dunno how helpful this is (perhaps not very), but it purportedly shows how often "proper" and "true" appear in print as modifiers of "list" recently: https://books.google.com/ngrams/graph?content=list%3D%3Eproper%2Clist%3D%3Etrue&case_insensitive=on&year_start=2000&year_end=2018&corpus=15&smoothing=0&share=&direct_url=t4%3B%2Clist%3D%3Eproper%3B%2Cc0%3B%2Cs0%3B%3Blist%3D%3Eproper%3B%2Cc0%3B%3BList%3D%3Eproper%3B%2Cc0%3B.t4%3B%2Clist%3D%3Etrue%3B%2Cc0%3B%2Cs0%3B%3Blist%3D%3Etrue%3B%2Cc0%3B%3Blist%3D%3ETrue%3B%2Cc0%3B%3BList%3D%3ETrue%3B%2Cc0%3B%3BList%3D%3Etrue%3B%2Cc0 They both are rare, and there doesn't seem to be much difference in their frequencies. If anything, "true list" is more used. (But this is not based only on mathematical texts - it just counts books in print.) 2. You say this in the doc string of `proper-list-length': A proper list is neither circular nor dotted (i.e., its last cdr is nil). And your entry in the manual for that function: In addition to satisfying @code{listp}, a proper list is neither circular nor dotted. Fair enough, but it depends on what is meant by a "circular" list. Does it mean only a list whose last cdr shares list structure with some other of its cdr's? If so then what you say holds. And yes, I guess that's what most of us think of. But what of a list that has a finite number of elements, so it last cdr is nil, but one or more of whose elements is itself a circular list in the above sense? Is it in some sense "circular" because of its circular-list element? IOW, a list can have finite length but still share list stucture with itself. Well, no, not really, but an element of it can be a list that shares list structure with itself. And so the finite-length list represents an infinite tree. For example, here's a one-element list whose only element is the circular list (a a a a a a ...): (setq foo '((a))) (setcdr (car foo) foo) foo ; ((a #1)) (length foo) ; 1 (nth 0 foo) ; (a #0) (nth 1 foo) ; nil (nth 0 (nth 0 foo)) ; a (nth 1 (nth 0 foo)) ; (a #0) (nth 2 (nth 0 foo)) ; nil (nth 0 (nth 0 (nth 0 foo)) ; a (nth 0 (nth 1 (nth 0 foo)) ; (a #0) (nth 0 (nth 2 (nth 0 foo)) ; nil ... It should be enough to say that a "true", or "proper" list is one that has nil as its last cdr, without adding that this means non-"circular". ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 17:00 ` Drew Adams @ 2018-07-06 17:20 ` Paul Eggert 2018-07-06 17:33 ` Eli Zaretskii 2018-07-08 22:38 ` Basil L. Contovounesios 2 siblings, 0 replies; 100+ messages in thread From: Paul Eggert @ 2018-07-06 17:20 UTC (permalink / raw) To: Drew Adams, Basil L. Contovounesios; +Cc: emacs-devel Drew Adams wrote: > They both are rare, and there doesn't seem to be much > difference in their frequencies Just looking at n-grams is not sufficient. You need to look how the terms are actually used. I just now checked Google Books for books published in the 21st century. Searching for "proper list" (with the quotes in the search) got a significant number of relevant hits from Lispish books, with a smattering of Prolog. There were also quite a few irrelevant hits. Searching for "true list" (again, quoted) got zero relevant hits. The "true list" hits from programming books were like this one, from page 94 of "Practical OCaml": # List.mem 50 example_list;; - : bool = true # List.mem 100 example_list;; - : bool = false That is, these hits did not represent usage "true list" to mean a null-terminated list. From this survey, it appears that the unanimous preference in recent published books is for "proper list" over "true list". ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 17:00 ` Drew Adams 2018-07-06 17:20 ` Paul Eggert @ 2018-07-06 17:33 ` Eli Zaretskii 2018-07-08 22:38 ` Basil L. Contovounesios 2 siblings, 0 replies; 100+ messages in thread From: Eli Zaretskii @ 2018-07-06 17:33 UTC (permalink / raw) To: Drew Adams; +Cc: contovob, eggert, emacs-devel > Date: Fri, 6 Jul 2018 10:00:34 -0700 (PDT) > From: Drew Adams <drew.adams@oracle.com> > Cc: emacs-devel@gnu.org > > 1. Why is "proper" better than "true"? I don't argue > against "proper", but what is really gained by this > change in Emacs terminology? Please go back and re-read the discussion that led to this patch, its URL is in the commit log message of the patch. Let's not reopen old discussions unless we have a good reason. > Dunno how helpful this is (perhaps not very), but it > purportedly shows how often "proper" and "true" appear > in print as modifiers of "list" recently: We will still have "true" in the manual and in the index. ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-06 17:00 ` Drew Adams 2018-07-06 17:20 ` Paul Eggert 2018-07-06 17:33 ` Eli Zaretskii @ 2018-07-08 22:38 ` Basil L. Contovounesios 2 siblings, 0 replies; 100+ messages in thread From: Basil L. Contovounesios @ 2018-07-08 22:38 UTC (permalink / raw) To: Drew Adams; +Cc: Paul Eggert, emacs-devel Drew Adams <drew.adams@oracle.com> writes: > 2. You say this in the doc string of `proper-list-length': > > A proper list is neither circular nor dotted (i.e., its > last cdr is nil). > > And your entry in the manual for that function: > > In addition to satisfying @code{listp}, a proper list is > neither circular nor dotted. > > Fair enough, but it depends on what is meant by a "circular" > list. Does it mean only a list whose last cdr shares list > structure with some other of its cdr's? If so then what you > say holds. And yes, I guess that's what most of us think of. > > But what of a list that has a finite number of elements, > so it last cdr is nil, but one or more of whose elements > is itself a circular list in the above sense? Is it in > some sense "circular" because of its circular-list element? No, because "proper", "circular", and "dotted" all refer to list structure (CDRs/links), not contents (CARs). > It should be enough to say that a "true", or "proper" > list is one that has nil as its last cdr, without > adding that this means non-"circular". I think that both "last cdr is nil" and "neither dotted nor circular" completely qualify a list as being proper, so I would prefer to be explicit and state both definitions, say, in the docstring. If nothing else, it makes the jargon "dotted" and "circular" more discoverable. Unless my reasoning/understanding is mistaken, that is. -- Basil ^ permalink raw reply [flat|nested] 100+ messages in thread
* Re: Predicate for true lists 2018-07-05 22:31 ` Basil L. Contovounesios 2018-07-06 5:57 ` Eli Zaretskii 2018-07-06 17:00 ` Drew Adams @ 2018-07-06 17:30 ` Paul Eggert 2 siblings, 0 replies; 100+ messages in thread From: Paul Eggert @ 2018-07-06 17:30 UTC (permalink / raw) To: Basil L. Contovounesios; +Cc: emacs-devel > +@defun proper-list-length object > +This function returns the length of @var{object} if it is a proper > +list, @code{nil} otherwise. In addition to satisfying @code{listp}, a > +proper list is neither circular nor dotted. Please mention here that the function can be used as a predicate; otherwise, that fact would be documented only in NEWS. Also, as Eli suggested, mention the same thing in the doc string. > - (let ((a-proper-p (ert--proper-list-p a)) > - (b-proper-p (ert--proper-list-p b))) > - (if (not (eql (not a-proper-p) (not b-proper-p))) > + (let ((a-proper-p (proper-list-length a)) > + (b-proper-p (proper-list-length b))) > + (if (not (eq (not a-proper-p) (not b-proper-p))) > `(one-list-proper-one-improper ,a ,b) > (if a-proper-p > - (if (not (equal (length a) (length b))) > + (if (/= (length a) (length b)) This computes the lengths of A and B twice; just compute it once. ^ permalink raw reply [flat|nested] 100+ messages in thread
[parent not found: <<87fu3vdjjk.fsf@tcd.ie>]
[parent not found: <<<87fu3vdjjk.fsf@tcd.ie>]
end of thread, other threads:[~2019-04-22 15:19 UTC | newest] Thread overview: 100+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2018-04-16 19:34 Predicate for true lists Basil L. Contovounesios 2018-06-04 12:12 ` Basil L. Contovounesios 2018-06-04 14:08 ` Stefan Monnier 2018-06-04 14:46 ` Basil L. Contovounesios 2018-06-04 15:31 ` Drew Adams 2018-06-04 16:14 ` Basil L. Contovounesios 2018-06-04 16:38 ` Drew Adams 2018-06-04 16:57 ` Stefan Monnier 2018-06-05 1:23 ` Paul Eggert 2018-06-05 2:57 ` Drew Adams 2018-06-05 3:08 ` Drew Adams 2018-06-05 3:12 ` Drew Adams 2018-06-05 3:25 ` Drew Adams 2018-06-05 15:05 ` Basil L. Contovounesios 2018-06-06 7:42 ` Paul Eggert 2018-06-06 9:40 ` Van L 2018-06-06 13:44 ` Stefan Monnier 2018-06-06 17:40 ` Stefan Monnier 2018-06-07 7:03 ` Van L 2018-07-05 22:31 ` Basil L. Contovounesios 2018-07-06 5:57 ` Eli Zaretskii 2018-07-06 17:16 ` Drew Adams 2018-07-06 17:38 ` Eli Zaretskii [not found] ` <<83601sl0wo.fsf@gnu.org> 2018-07-06 18:00 ` Drew Adams 2018-07-07 6:54 ` Eli Zaretskii [not found] ` <<<83601sl0wo.fsf@gnu.org> [not found] ` <<95fda70b-5893-4788-83c5-a0bb5d708304@default> [not found] ` <<8336wvleml.fsf@gnu.org> 2018-07-07 14:42 ` Drew Adams 2018-07-06 18:04 ` Paul Eggert 2018-07-07 6:58 ` Eli Zaretskii 2018-07-07 7:20 ` martin rudalics 2018-07-07 8:41 ` Paul Eggert 2018-07-07 10:04 ` Eli Zaretskii 2018-07-07 15:04 ` Basil L. Contovounesios 2018-07-07 16:12 ` Eli Zaretskii 2018-07-07 16:52 ` Basil L. Contovounesios 2018-07-07 17:07 ` Eli Zaretskii 2018-07-07 17:14 ` Paul Eggert 2018-07-07 17:34 ` Eli Zaretskii 2018-07-08 0:15 ` Drew Adams 2018-07-08 4:48 ` Paul Eggert 2018-07-08 15:15 ` Drew Adams 2018-07-08 16:00 ` Paul Eggert 2018-07-08 17:42 ` Drew Adams 2018-07-08 17:47 ` Paul Eggert 2018-07-07 17:06 ` Basil L. Contovounesios 2018-07-09 19:25 ` Basil L. Contovounesios 2018-07-09 19:40 ` Basil L. Contovounesios 2018-07-10 2:02 ` Paul Eggert 2018-07-10 5:46 ` Basil L. Contovounesios 2018-07-11 3:02 ` Paul Eggert 2018-07-11 6:27 ` Basil L. Contovounesios 2018-07-15 22:55 ` Wilfred Hughes 2018-07-16 1:37 ` Paul Eggert 2018-07-11 14:01 ` [Emacs-diffs] master babe0d4: Rearrange definition of zerop in subr.el Karl Fogel 2018-07-11 17:12 ` Basil L. Contovounesios 2018-07-11 17:33 ` Paul Eggert 2018-07-12 15:34 ` Basil L. Contovounesios 2018-07-12 15:43 ` Basil L. Contovounesios 2019-04-09 12:51 ` Predicate for true lists Basil L. Contovounesios 2019-04-09 15:33 ` Stefan Monnier 2019-04-09 16:20 ` Basil L. Contovounesios 2019-04-09 16:32 ` Stefan Monnier 2019-04-09 16:54 ` Daniel Colascione 2019-04-09 17:27 ` Basil L. Contovounesios 2019-04-09 17:27 ` Basil L. Contovounesios 2019-04-09 20:08 ` Unused value of error-free function warning (was: Predicate for true lists) Basil L. Contovounesios 2019-04-09 20:40 ` Unused value of error-free function warning Stefan Monnier 2019-04-09 20:12 ` Predicate for true lists Basil L. Contovounesios 2019-04-09 20:41 ` Stefan Monnier 2019-04-10 2:32 ` Eli Zaretskii 2019-04-10 14:16 ` Alex Branham 2019-04-10 14:34 ` Basil L. Contovounesios 2019-04-10 15:01 ` Drew Adams 2019-04-10 15:45 ` Basil L. Contovounesios 2019-04-10 16:04 ` Eli Zaretskii 2019-04-17 17:56 ` Basil L. Contovounesios 2019-04-17 18:11 ` Stefan Monnier 2019-04-21 21:42 ` Basil L. Contovounesios 2019-04-17 18:55 ` Drew Adams 2019-04-21 21:24 ` Basil L. Contovounesios 2019-04-22 0:03 ` Drew Adams 2019-04-22 1:12 ` Michael Heerdegen 2019-04-22 9:39 ` Drew Adams 2019-04-18 14:37 ` Eli Zaretskii 2019-04-21 18:30 ` Basil L. Contovounesios 2019-04-21 19:39 ` Eli Zaretskii 2019-04-21 21:37 ` Basil L. Contovounesios 2019-04-22 0:06 ` Drew Adams 2019-04-22 7:49 ` Eli Zaretskii 2019-04-22 12:59 ` Basil L. Contovounesios 2019-04-22 13:12 ` Eli Zaretskii 2019-04-22 15:19 ` Basil L. Contovounesios 2019-04-21 19:41 ` Eli Zaretskii 2019-04-21 21:41 ` Basil L. Contovounesios 2019-04-22 6:39 ` Eli Zaretskii 2019-04-22 12:58 ` Basil L. Contovounesios 2018-07-06 17:00 ` Drew Adams 2018-07-06 17:20 ` Paul Eggert 2018-07-06 17:33 ` Eli Zaretskii 2018-07-08 22:38 ` Basil L. Contovounesios 2018-07-06 17:30 ` Paul Eggert [not found] <<87fu3vdjjk.fsf@tcd.ie> [not found] <<<87fu3vdjjk.fsf@tcd.ie>
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).