GNU bug report logs - #62117
29.0.60; cl-letf on a map place has side-effects

Previous Next

Package: emacs;

Reported by: Augusto Stoffel <arstoffel <at> gmail.com>

Date: Sat, 11 Mar 2023 07:45:02 UTC

Severity: normal

Found in version 29.0.60

Fixed in version 29.1

Done: Augusto Stoffel <arstoffel <at> gmail.com>

Bug is archived. No further changes may be made.

To add a comment to this bug, you must first unarchive it, by sending
a message to control AT debbugs.gnu.org, with unarchive 62117 in the body.
You can then email your comments to 62117 AT debbugs.gnu.org in the normal way.

Toggle the display of automated, internal messages from the tracker.

View this report as an mbox folder, status mbox, maintainer mbox


Report forwarded to bug-gnu-emacs <at> gnu.org:
bug#62117; Package emacs. (Sat, 11 Mar 2023 07:45:02 GMT) Full text and rfc822 format available.

Acknowledgement sent to Augusto Stoffel <arstoffel <at> gmail.com>:
New bug report received and forwarded. Copy sent to bug-gnu-emacs <at> gnu.org. (Sat, 11 Mar 2023 07:45:02 GMT) Full text and rfc822 format available.

Message #5 received at submit <at> debbugs.gnu.org (full text, mbox):

From: Augusto Stoffel <arstoffel <at> gmail.com>
To: bug-gnu-emacs <at> gnu.org
Subject: 29.0.60; cl-letf on a map place has side-effects
Date: Sat, 11 Mar 2023 08:44:26 +0100
Consider this example:

--8<---------------cut here---------------start------------->8---
(require 'cl-lib)
(defun f (map)
  (cl-letf (((map-elt map 'a) 1))
     map))

(let ((map '(b 2)))
  (f map)
  map)
=> (b 2 a nil)

(let ((map (make-hash-table)))
  (f map)
  (map-length map))
=> 1
--8<---------------cut here---------------end--------------->8---

I would expect `f' to have no side effects, so get (b 2) and 0
respectively in the two examples.

For comparison, plist-get as a place has no side-effect:

--8<---------------cut here---------------start------------->8---
(defun g (map)
  (cl-letf (((plist-get map 'a) 1))
    map))

(let ((map '(b 2)))
  (g map)
  map)
(b 2)
--8<---------------cut here---------------end--------------->8---

Of course it's usual to treat a nil entry and no entry as equivalent in
Lisp, but this behavior can be a problem e.g. when constructing data to
pass to other programs.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62117; Package emacs. (Sun, 12 Mar 2023 00:01:02 GMT) Full text and rfc822 format available.

Message #8 received at 62117 <at> debbugs.gnu.org (full text, mbox):

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Augusto Stoffel <arstoffel <at> gmail.com>
Cc: 62117 <at> debbugs.gnu.org
Subject: Re: bug#62117: 29.0.60; cl-letf on a map place has side-effects
Date: Sun, 12 Mar 2023 01:00:12 +0100
Augusto Stoffel <arstoffel <at> gmail.com> writes:

> Consider this example:
>
> (require 'cl-lib)
> (defun f (map)
>   (cl-letf (((map-elt map 'a) 1))
>      map))
>
> (let ((map '(b 2)))
>   (f map)
>   map)
> => (b 2 a nil)
>
> (let ((map (make-hash-table)))
>   (f map)
>   (map-length map))
> => 1
>
>
> I would expect `f' to have no side effects, so get (b 2) and 0
> respectively in the two examples.

This is a symptom of a general limitation of `cl-letf'.  Currently you can't
rely on a "no side effect" behavior.  There are other examples like that
(`alist-get') and cases that are worse (binding `buffer-local-value' of
a variable in a buffer with no buffer local binding doesn't remove the
buffer-localness - that's one reason why that gv had been deprecated).

> Of course it's usual to treat a nil entry and no entry as equivalent in
> Lisp, but this behavior can be a problem e.g. when constructing data to
> pass to other programs.

I would say: if it is a problem, map.el is the wrong abstraction for
your case.  That's the genuine idea of map.el: that the inner structure
of a map doesn't matter.

So I would close this one - unless you have some enlightening idea that
would be an obvious improvement with no downsides and backward
compatibility problems.


Thanks,

Michael.




Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62117; Package emacs. (Sun, 12 Mar 2023 06:11:02 GMT) Full text and rfc822 format available.

Message #11 received at 62117 <at> debbugs.gnu.org (full text, mbox):

From: Augusto Stoffel <arstoffel <at> gmail.com>
To: Michael Heerdegen <michael_heerdegen <at> web.de>
Cc: 62117 <at> debbugs.gnu.org
Subject: Re: bug#62117: 29.0.60; cl-letf on a map place has side-effects
Date: Sun, 12 Mar 2023 07:09:56 +0100
On Sun, 12 Mar 2023 at 01:00, Michael Heerdegen wrote:

> Augusto Stoffel <arstoffel <at> gmail.com> writes:
> This is a symptom of a general limitation of `cl-letf'.  Currently you can't
> rely on a "no side effect" behavior.  There are other examples like that
> (`alist-get') and cases that are worse (binding `buffer-local-value' of
> a variable in a buffer with no buffer local binding doesn't remove the
> buffer-localness - that's one reason why that gv had been deprecated).

Right, so let's drop this bug.

>> Of course it's usual to treat a nil entry and no entry as equivalent in
>> Lisp, but this behavior can be a problem e.g. when constructing data to
>> pass to other programs.
>
> I would say: if it is a problem, map.el is the wrong abstraction for
> your case.  That's the genuine idea of map.el: that the inner structure
> of a map doesn't matter.

This is not right.  map.el doesn't abstract away the nil entries.  They
make a difference all over, e.g. in map-length, map-do, map-empty-p...




bug marked as fixed in version 29.1, send any further explanations to 62117 <at> debbugs.gnu.org and Augusto Stoffel <arstoffel <at> gmail.com> Request was from Augusto Stoffel <arstoffel <at> gmail.com> to control <at> debbugs.gnu.org. (Sun, 12 Mar 2023 06:11:02 GMT) Full text and rfc822 format available.

Information forwarded to bug-gnu-emacs <at> gnu.org:
bug#62117; Package emacs. (Wed, 15 Mar 2023 02:15:02 GMT) Full text and rfc822 format available.

Message #16 received at 62117 <at> debbugs.gnu.org (full text, mbox):

From: Michael Heerdegen <michael_heerdegen <at> web.de>
To: Augusto Stoffel <arstoffel <at> gmail.com>
Cc: 62117 <at> debbugs.gnu.org
Subject: Re: bug#62117: 29.0.60; cl-letf on a map place has side-effects
Date: Wed, 15 Mar 2023 03:13:57 +0100
Augusto Stoffel <arstoffel <at> gmail.com> writes:

> >> Of course it's usual to treat a nil entry and no entry as equivalent in
> >> Lisp, but this behavior can be a problem e.g. when constructing data to
> >> pass to other programs.
> >
> > I would say: if it is a problem, map.el is the wrong abstraction for
> > your case.  That's the genuine idea of map.el: that the inner structure
> > of a map doesn't matter.
>
> This is not right.  map.el doesn't abstract away the nil entries.  They
> make a difference all over, e.g. in map-length, map-do, map-empty-p...

Hmm - right, of course.

For your original recipe, it would in theory help if the implementation
of `map-put!' for hash tables would remove the entry if the set value is
nil.  But for the reason you gave, we probably don't want to do this.

Unless maybe we do the same thing that had been done for `alist-get'
and add an additional REMOVE argument to the generic functions `map-elt'
and `map-put!' (I'm afraid that a lot of people that like using
libraries such as map.el don't like using such an argument, though).

`cl-letf' would still not be smart enough to distinguish the two cases
(1) value was the default value and (2) there was no entry when
restoring the gv binding.


Michael.




bug archived. Request was from Debbugs Internal Request <help-debbugs <at> gnu.org> to internal_control <at> debbugs.gnu.org. (Wed, 12 Apr 2023 11:24:05 GMT) Full text and rfc822 format available.

This bug report was last modified 1 year and 15 days ago.

Previous Next


GNU bug tracking system
Copyright (C) 1999 Darren O. Benham, 1997,2003 nCipher Corporation Ltd, 1994-97 Ian Jackson.