* 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-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 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: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-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
* 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: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
* 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 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: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
* 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
[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-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 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-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-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-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: [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 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: 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: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
* 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
* 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: 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: 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 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 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 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 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-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 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 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-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-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-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-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-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-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 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-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
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
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).