Ticket UUID: | 1b0266d8bbc649bcb4df5dc4a1edca238536262b | |||
Title: | Single-argument [dict merge/remove/replace] | |||
Type: | Bug | Version: | cdc4e05235717e4959d745e4cc6670bd1333c783 | |
Submitter: | andy | Created on: | 2014-05-21 22:58:03 | |
Subsystem: | 15. Dict Object | Assigned To: | dkf | |
Priority: | 5 Medium | Severity: | Minor | |
Status: | Closed | Last Modified: | 2014-06-15 16:15:11 | |
Resolution: | Fixed | Closed By: | dkf | |
Closed on: | 2014-06-15 16:15:11 | |||
Description: |
When given only one argument, [dict remove] and [dict replace] seem to return that argument without even looking at it. Consequently, they can be made to return non-dicts (i.e. invalid list or odd list length) and noncanonical dicts (i.e. with duplicate keys). When given only one argument, [dict merge] forces the argument to be a dict (thereby rejecting non-lists and odd-length lists), but the string representation is preserved. Consequently, [dict merge] can be made to return noncanonical dicts. % dict remove \{\}x {}x % dict remove {a 1 b} a 1 b % dict remove {a 1 b 2 a 3} a 1 b 2 a 3 % dict replace \{\}x {}x % dict replace {a 1 b} a 1 b % dict replace {a 1 b 2 a 3} a 1 b 2 a 3 % dict merge \{\}x list element in braces followed by "x" instead of space % dict merge {a 1 b} missing value to go with key % dict merge {a 1 b 2 a 3} a 1 b 2 a 3 % ::tcl::unsupported::representation [dict merge {a 1 b 2}] value is a dict with a refcount of 3, object pointer at 0xbeb3b0, internal representation 0xbe11a0:0xbeb860, string representation "a 1 b 2" % ::tcl::unsupported::representation [dict merge {a 1 b 2 a 3}] value is a dict with a refcount of 5, object pointer at 0x1c3a560, internal representation 0x1c843a0:0x1c35790, string representation "a 1 b 2 a 3" Contrast with every other [dict] command that returns a dict and/or sets a dict variable. They all produce only canonical dicts. % dict get {a 1 b 2 a 3} a 3 b 2 % ::tcl::unsupported::representation [dict get {a 1 b 2 a 3}] value is a list with a refcount of 1, object pointer at 0x99cc90, internal representation 0x9adb00:(nil), no string representation % ::tcl::unsupported::representation [dict get {a 1 b 2}] value is a list with a refcount of 1, object pointer at 0x9a09e0, internal representation 0x9db0e0:(nil), no string representation | |||
User Comments: |
dkf added on 2014-06-15 15:36:27:
(text/html)
In fact, I completely refuse to promise canonicalization with <tt>dict merge</tt>, as that causes problems in compilation. dkf added on 2014-06-15 07:59:29: (text/html) On the other hand, having <tt>dict replace</tt> and <tt>dict remove</tt> guarantee canonicality in their result (as long as it doesn't leak back into the argument) is reasonable; they parallel with <tt>lrange</tt>'s features in this area. <p> I think <tt>dict merge</tt> doesn't need the change, as that feels more like it's working with whatever people provide; it merges <i>your</i> dictionaries. dkf added on 2014-06-03 07:31:08: (text/html) Uncovered an issue when working on this; the error message is wrong. <pre> test dict-4.14 {dict replace command: type check is mandatory} -body { dict replace { a b {}c d } } -returnCodes error -result {<b><u>list</u></b> element in braces followed by "c" instead of space} </pre> It's a bit tricky to fix; the message comes out of <tt>TclFindElement</tt> and that both has a semi-exposed API and no way to actually affect it at the moment. dkf added on 2014-06-03 07:09:53: (text/html) To be exact, canonicality comes from the <i>modification</i> causing a purge of the old string representation. Consider this: <pre> % dict merge { a b c d } {} {} {} a b c d % dict merge { a b c d } {a b} a b c d </pre> (No, we aren't going to detect where a modification “does nothing”.) dkf added on 2014-06-03 06:59:39: (text/html) I think the non-canonicality is OK; the code isn't guaranteed to canonicalize when it doesn't have anything to do. However, the failure to confirm the format is indeed an (arguable) bug.<p>It'd be a Potential Incompatibility though. |