2

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.

user1134991
  • 3,003
  • 2
  • 25
  • 35
  • You can try checking the `tcl_version` variable and apply a condition whether the version is greater than or equal to 8.5 or not to avoid this . – Dinesh Mar 17 '15 at 12:15
  • Relevant: [Determine type of a variable in TCL](http://stackoverflow.com/questions/7428032/determine-type-of-a-variable-in-tcl). – Jerry Mar 17 '15 at 12:26

2 Answers2

7

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.

Donal Fellows
  • 133,037
  • 18
  • 149
  • 215
3

Your approach is flawed because Tcl has dynamic type system where the actual type of a value is able to morph dynamically and depends on the commands applied to it—observe:

$ tclsh
% info pa
8.5.11
% dict info {a b}
1 entries in table, 4 buckets
number of buckets with 0 entries: 3
number of buckets with 1 entries: 1
number of buckets with 2 entries: 0
number of buckets with 3 entries: 0
number of buckets with 4 entries: 0
number of buckets with 5 entries: 0
number of buckets with 6 entries: 0
number of buckets with 7 entries: 0
number of buckets with 8 entries: 0
number of buckets with 9 entries: 0
number of buckets with 10 or more entries: 0
average search distance for entry: 1.0
% llength {a b}
2
% string len {a b}
3
% 

As you can see, the same value {a b} is a dictionary, a list and a string: in each case, the value acquires its "real" type in the very moment a Tcl command expecting a value of certain type converts the "default" type of the value, which is string, to the one the command operates on.

You should understand by now that trying to make a call dict? {a b} has little sence as the value {a b} is a perfect dict as well as a perfect list as well as a perfect string, and it could be, say, a perfect tuple if there are custom commands in the current interpreter working on tuples (lists of fixed length).

Hence the real approach you should take is to just blindly use dict command on those values passed to your commands you expect to contain dictionaries. If a user will manage to pass to your command something which is not interpretable as a dictionary, the dict command will fail, and that's a good thing to do as such an error is not really recoverable (it's a programming error).

Any attempt to count on a value's specific type is going again the grain of the very idea of the Tcl's implicit/dynamic typing. It's even true for the Tcl C API.


If you really meant to ask how to be sure the current Tcl version supports dict command, and not about the type of a particular value, test the Tcl's version somewhere at startup and save this as a flag, like this:

set hasDicts [expr {[package vcompare [info tclversion] 8.5] >= 0}]

But note that your code relying on the hasDicts value is now in some gray zone because if the user is not supplying you values you process with the dict command then what command you use to process them?

Please also note that the dict command can be added to a Tcl 8.4 interpreter in the form of the loadable module (see this).

kostix
  • 51,517
  • 14
  • 93
  • 176
  • 1
    This language strength (everything is a string) is also it's downfall, and it's limitation to future growth.... – user1134991 Mar 17 '15 at 15:05
  • @user1134991, I disagree. While in the end I prefer more strict typing (my favorite programming language at the moment is [Go](http://golang.org)) Tcl's approach to typing in beautiful. You appear to just have a certain mental barrier regarding this approach. That's why I begun with stating that the very premise of your question is flawed: seasoned Tcl programmers simply do not compe up with a need to test a value's type in the first place: they just *treat* values as being of certain type throughout the lives of those values, and the whole thing just works. – kostix Mar 17 '15 at 16:00
  • 1
    @user1134991, If you need more guidance on this, just ask on the Tcler's chat or on `comp.lang.tcl` newsgroup/mailing list -- I'm afraid this sort of discussion does not really fit the SO's paradigm, and thus redirecting. – kostix Mar 17 '15 at 16:01
  • @user1134991, to put this in a different perspective... You usually just *define* which set of features your program has to rely on and state that by `package require`-ing particular versions of external packages *and the Tc interpreter itself* -- by saying something like `package require Tcl 8.5`: this directive will fail right away on Tcl < 8.5. Now you just use `dict` everywhere you intend it to, and state that in the docs for procs constituting your public API, if any. And again, it just works. – kostix Mar 17 '15 at 16:11