Search code examples
dictionarytclcatch-block

Check if an argument is a dictionary or not in Tcl


I want have a proc which does something if its' argument is a Tcl 8.5 and above dictionary or not. I could not find anything straightforward from Tcl dict command. The code which I could get working is:

proc dict? {dicty} { 
    expr { [catch { dict info $dicty } ] ? 0 : 1 }
}

Is there anything w/o using catch, something built in?
Thanks.


Solution

  • You can test if a value is a dictionary by seeing if it is a list and if it has an even number of elements; all even length lists may be used as dictionaries (though many are naturally not canonical dictionaries because of things like duplicate keys).

    proc is-dict {value} {
        return [expr {[string is list $value] && ([llength $value]&1) == 0}]
    }
    

    You can peek at the actual type in Tcl 8.6 with tcl::unsupported::representation but that's not advised because things like literals are converted to dictionaries on the fly. The following is legal, shows what you can do, and shows the limitations (

    % set value {b c d e}
    b c d e
    % tcl::unsupported::representation $value
    value is a pure string with a refcount of 4, object pointer at 0x1010072e0, string representation "b c d e"
    % dict size $value
    2
    % tcl::unsupported::representation $value
    value is a dict with a refcount of 4, object pointer at 0x1010072e0, internal representation 0x10180fd10:0x0, string representation "b c d e"
    % dict set value f g;tcl::unsupported::representation $value
    value is a dict with a refcount of 2, object pointer at 0x1008f00c0, internal representation 0x10101eb10:0x0, no string representation
    % string length $value
    11
    % tcl::unsupported::representation $value
    value is a string with a refcount of 2, object pointer at 0x1008f00c0, internal representation 0x100901890:0x0, string representation "b c d e f g"
    % dict size $value;tcl::unsupported::representation $value
    value is a dict with a refcount of 2, object pointer at 0x1008f00c0, internal representation 0x1008c7510:0x0, string representation "b c d e f g"
    

    As you can see, types are a bit slippery in Tcl (by design) so you're strongly advised to not rely on them at all.