I have the following function:
(defun inc-map ()
(let ((ht #s(hash-table test contents-hash)))
(dolist (i (list 1 2 3 4))
(let ((old-val (or (gethash "foo" ht)
0)))
(puthash "foo" (+ 1 old-val) ht)))
ht))
Despite the fact that this function seems to define the ht
symbol locally, the function doesn't seem to be referentially transparent. In particular, calling it once returns the hash table "foo" -> 4
, calling it a second time returns "foo" -> 8
, a third time returns "foo" -> 12
and so on.
What exactly is going on here and how do I change this function to be referentially transparent (and return "foo" -> 4
every time)?
This might be considered a (slight) documentation bug, in that it suggests a little too firmly that using the printed representation creates a new hash table -- a statement which is open to misinterpretation.
However, you'll note that the documentation says that it's the elisp reader that recognises the printed representation of a hash table.
Therefore using #s
is not the same as calling make-hash-table
. The difference here is equivalent to the difference between quoting a list '(1 2 3)
and calling (list 1 2 3)
.
The former in each case is processed by the reader and hence the same, single resulting object (hash table or list respectively) is seen during each evaluation.
Conversely, in the latter instances the reader is generating code which, when evaluated, will create a new hash table or list; and therefore you see a new object during each evaluation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With