Tcl variables have no types (moreover, are they really an associative array of variables, i.e. using the syntax $foo(bar) , for which you use array exists ), but Tcl values. Well, a little bit. Tcl can change values ​​between different types, as it considers necessary, and does not disclose this information [*]; all you really can do is check if the value matches a particular type.
Such compliance checks are done using string is (where you need the -strict option for ugly historical reasons):
if {[string is integer -strict $foo]} { puts "$foo is an integer!" } if {[string is list $foo]} { # Only [string is] where -strict has no effect puts "$foo is a list! (length: [llength $foo])" if {[llength $foo]&1 == 0} { # All dictionaries conform to lists with even length puts "$foo is a dictionary! (entries: [dict size $foo])" } }
Note that all values ​​correspond to the type of strings; Tcl values ​​are always serializable.
[EDIT from comments]: you can use dirty hacks to serialize JSON to create the “correct” serialization (strictly putting everything in a line would be correct from the point of view of Tcl, but this is not entirely useful for other languages) with Tcl 8.6. The code for this, originally posted on Rosetta Code :
package require Tcl 8.6 proc tcl2json value { # Guess the type of the value; deep *UNSUPPORTED* magic! regexp {^value is a (.*?) with a refcount} \ [::tcl::unsupported::representation $value] -> type switch $type { string { # Skip to the mapping code at the bottom } dict { set result "{" set pfx "" dict for {kv} $value { append result $pfx [tcl2json $k] ": " [tcl2json $v] set pfx ", " } return [append result "}"] } list { set result "\[" set pfx "" foreach v $value { append result $pfx [tcl2json $v] set pfx ", " } return [append result "\]"] } int - double { return [expr {$value}] } booleanString { return [expr {$value ? "true" : "false"}] } default { # Some other type; do some guessing... if {$value eq "null"} { # Tcl has *no* null value at all; empty strings are semantically # different and absent variables aren't values. So cheat! return $value } elseif {[string is integer -strict $value]} { return [expr {$value}] } elseif {[string is double -strict $value]} { return [expr {$value}] } elseif {[string is boolean -strict $value]} { return [expr {$value ? "true" : "false"}] } } } # For simplicity, all "bad" characters are mapped to \u... substitutions set mapped [subst -novariables [regsub -all {[][\u0000-\u001f\\""]} \ $value {[format "\\\\u%04x" [scan {& } %c]]}]] return "\"$mapped\"" }
Warning: The above code is not supported. It depends on dirty hacks. It may break without warning. (But it works. For porting to Tcl 8.5, you need a small C extension to read type annotations.)
[*] Strictly speaking, it provides an unsupported interface for detecting annotations of the current value type in 8.6 - as part of ::tcl::unsupported::representation , but this information is in a consciously readable form and can be changed without announcement, This is for debugging, not code. In addition, Tcl uses quite a few different types (for example, cached names of commands and variables) that you would not want to examine under normal circumstances; things are pretty complicated under the hood ...
Donal fellows
source share