Skip to content

Commit

Permalink
No longer mark some functions as pure
Browse files Browse the repository at this point in the history
Functions that guarantee a fresh return value should not be pure,
since their return value is likely to be mutated destructively:
https://bugs.gnu.org/64127

* NEWS.md (2.20.0): List affected functions.
* dash.el (-non-nil, -cons*, -snoc, -slice, -take, -take-last)
(-drop-last, -split-at, -interpose, -interleave, -repeat, -iota)
(-clone): No longer mark as pure.
* dev/examples.el (-powerset): Avoid mutating constant.

Fixes #405.
  • Loading branch information
basil-conto committed Jun 17, 2023
1 parent 96eaba0 commit d5182da
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 14 deletions.
9 changes: 9 additions & 0 deletions NEWS.md
Expand Up @@ -35,6 +35,15 @@ See the end of the file for license conditions.
(-permutations '(1 1 2)) ; => '((1 1 2) (1 2 1) (2 1 1))
```

- Several functions which are documented as returning a fresh, mutable
object (such as a copy of one of their arguments) are no longer
marked as `pure`. Pure functions called with constant arguments are
evaluated during byte-compilation; the resulting value is an
immutable constant, and thus unsafe to modify destructively. The
functions in question are: `-clone`, `-cons*`, `-drop-last`,
`-interleave`, `-interpose`, `-iota`, `-non-nil`, `-repeat`,
`-slice`, `-snoc`, `-split-at`, `-take`, `-take-last`.

#### New features

- The function `-contains?` now returns the matching tail of the list
Expand Down
26 changes: 13 additions & 13 deletions dash.el
Expand Up @@ -607,7 +607,7 @@ Its anaphoric counterpart is `--keep'."

(defun -non-nil (list)
"Return a copy of LIST with all nil items removed."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(--filter it list))

(defmacro --map-indexed (form list)
Expand Down Expand Up @@ -856,7 +856,7 @@ See also: `-splice', `-insert-at'"
The last 2 elements of ARGS are used as the final cons of the
result, so if the final element of ARGS is not a list, the result
is a dotted list. With no ARGS, return nil."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(let* ((len (length args))
(tail (nthcdr (- len 2) args))
(last (cdr tail)))
Expand All @@ -871,7 +871,7 @@ is a dotted list. With no ARGS, return nil."
This is like `cons', but operates on the end of list.
If any ELEMENTS are given, append them to the list as well."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(-concat list (list elem) elements))

(defmacro --first (form list)
Expand Down Expand Up @@ -1172,7 +1172,7 @@ modulo the length of the list.
If STEP is a number, only each STEPth item in the resulting
section is returned. Defaults to 1."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(let ((length (length list))
(new-list nil))
;; to defaults to the end of the list
Expand Down Expand Up @@ -1247,7 +1247,7 @@ Return a copy of LIST if it contains N items or fewer.
Return nil if N is zero or less.
See also: `-take-last'."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(--take-while (< it-index n) list))

(defun -take-last (n list)
Expand All @@ -1256,7 +1256,7 @@ Return a copy of LIST if it contains N items or fewer.
Return nil if N is zero or less.
See also: `-take'."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(copy-sequence (last list n)))

(defalias '-drop #'nthcdr
Expand All @@ -1273,7 +1273,7 @@ Return a copy of LIST if N is zero or less.
Return nil if LIST contains N items or fewer.
See also: `-drop'."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(nbutlast (copy-sequence list) n))

(defun -split-at (n list)
Expand All @@ -1283,7 +1283,7 @@ new list of the first N elements of LIST, and DROP is the
remaining elements of LIST (not a copy). TAKE and DROP are like
the results of `-take' and `-drop', respectively, but the split
is done in a single list traversal."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(let (result)
(--each-while list (< it-index n)
(push (pop list) result))
Expand Down Expand Up @@ -1641,7 +1641,7 @@ elements of LIST. Keys are compared by `equal'."

(defun -interpose (sep list)
"Return a new list of all elements in LIST separated by SEP."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(let (result)
(when list
(!cons (car list) result)
Expand All @@ -1653,7 +1653,7 @@ elements of LIST. Keys are compared by `equal'."

(defun -interleave (&rest lists)
"Return a new list of the first item in each list, then the second etc."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(when lists
(let (result)
(while (-none? 'null lists)
Expand Down Expand Up @@ -3321,7 +3321,7 @@ backward compatibility and is otherwise deprecated."
(defun -repeat (n x)
"Return a new list of length N with each element being X.
Return nil if N is less than 1."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(and (>= n 0) (make-list n x)))

(defun -sum (list)
Expand Down Expand Up @@ -3396,7 +3396,7 @@ Starts from START and adds STEP each time. The default START is
zero, the default STEP is 1.
This function takes its name from the corresponding primitive in
the APL language."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(unless (natnump count)
(signal 'wrong-type-argument (list #'natnump count)))
(or start (setq start 0))
Expand Down Expand Up @@ -3628,7 +3628,7 @@ Non-branch nodes are simply copied."
The new list has the same elements and structure but all cons are
replaced with new ones. This is useful when you need to clone a
structure such as plist or alist."
(declare (pure t) (side-effect-free t))
(declare (side-effect-free t))
(-tree-map #'identity list))

;;; Combinators
Expand Down
2 changes: 1 addition & 1 deletion dev/examples.el
Expand Up @@ -1363,7 +1363,7 @@ related predicates."
(-powerset '()) => '(())
(-powerset '(x y)) => '((x y) (x) (y) ())
(-powerset '(x y z)) => '((x y z) (x y) (x z) (x) (y z) (y) (z) ())
(let ((p (-powerset '()))) (setcar p t) (-powerset '())) => '(()))
(let* ((l (list 1)) (p (-powerset l))) (setcar l 2) p) => '((1) ()))

(defexamples -permutations
(-permutations '()) => '(())
Expand Down

0 comments on commit d5182da

Please sign in to comment.