<div dir="ltr"><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">Greetings, I feel timid sending this email to this list given that it’s been dormant since 2014. But after a few queries on #lisp, no better place was offered, and the currently-published information on CDRs directs readers to this list.</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">Here is the substance of my inquiry:</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">I believe CDR-8’s specification of EQUALS with respect to hash tables is incomplete, and its design may lead to some confusion. Here is an example, which uses an implementation of EQUALS provided by the library :generic-comparability[1]:</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">CL-USER> (ql:quickload :generic-comparability)</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">(:GENERIC-COMPARABILITY)</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">CL-USER> (defvar a (make-hash-table :test 'equalp))</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">A</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">CL-USER> (defvar b (make-hash-table :test 'equalp))</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">B</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">CL-USER> (setf (gethash "x" a) 1)</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">1</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">CL-USER> (setf (gethash "X" b) 1)</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">1</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">CL-USER> (loop for k being the hash-keys in a</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">            always (eql (gethash k a) (gethash k b)))</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">T</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">CL-USER> (generic-comparability:equals a b)</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">NIL</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">Here we see EQUALS disagreeing with a GETHASH-based equality test[2], which CDR-8 might argue is by design. EQUALS is its own equality test, so it should override whatever is set in the hash table it’s given. However, the spec is not explicit on this point, which may be surprising to the unsuspecting programmer.</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">Moreover, let’s continue: let’s assume the :by-key argument to EQUALS is NIL, so the request is to compare only the hash table values. Well, the only way to correctly compare the values in two tables is to arrange them by their keys, and then compare them. To do otherwise would be meaningless. But what function should be used to arrange the keys? If EQUALS is used, then the result will be identical to :by-key T, and will be inconsistent with an EQUALP hash-table as per my example.</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">It would seem more appropriate to use the function designated by HASH-TABLE-TEST, but CDR-8 is silent on this question, which makes the specification incomplete.</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">A secondary problem with CDR-8 is in the example implementation given for hash tables. The LOOP presented implicitly depends on HT-KEYS returning keys in some sort of consistent, total ordering, because otherwise it would be meaningless to do a key-wise comparison. However, the spec is silent on this requirement. And it’s not obvious what comparison function CDR-8 would prefer: its own COMPARE, or the user-specified HASH-TABLE-TEST function.</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">In practice, at least under SBCL, iterating through a hash table with LOOP will generate the keys in a different order depending on their insertion order. (I believe the order in which hash table keys are iterated is unspecified in the standard.)</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace"><br></font></span></p><p style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">Furthermore, it seems nonsensical to provide for disjoint comparison of keys and values. For example, should a test of the form (EQUALS (:a 1 :b 2) (:a 2 :b 1) :by-keys T :by-value NIL) return T, and (... :by-keys NIL :by-value T) also return T?</font></span></p><p style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace"><br></font></span></p><p style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">The most common case for comparing hash tables is to ensure they have the same number of keys, that the set of keys in each table are equal, and that the values in the slots referred to by the keys are also equal. The proposed implementation does not provide this.</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">I hope this exposition is helpful. I’d accept any suggestions on how to help improve CDR-8 or :generic-comparability.</font></span></p><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace"><br></font></span></p><p style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">Best,</font></span></p><p style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace"><br></font></span></p><p style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">anticrisis</font></span></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><font face="monospace, monospace"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap">[1] </span><a href="https://github.com/pnathan/generic-comparability" target="_blank" style="text-decoration-line:none"><span style="font-size:11pt;background-color:transparent;text-decoration-line:underline;vertical-align:baseline;white-space:pre-wrap">https://github.com/pnathan/<wbr>generic-comparability</span></a></font></p><font face="monospace, monospace" style="font-size:12.8px"><br></font><p dir="ltr" style="font-size:12.8px;line-height:1.38;margin-top:0pt;margin-bottom:0pt"><span style="font-size:11pt;color:rgb(0,0,0);background-color:transparent;vertical-align:baseline;white-space:pre-wrap"><font face="monospace, monospace">[2] Looping through the keys of A is only part of the equality test. Not shown is checking the other hash table properties, in particular, the hash table test function, as returned by HASH-TABLE-TEST.</font></span></p></div>