Define variable type in TCL

I am looking for a way to find the type of a variable in TCL. For example, if I have a variable $ a, and I want to know that it is an integer.

I used the following:

if {[string is boolean $a]} { #do something } 

and this seems to be great for the following types: alnum, alpha, ascii, boolean, control, digit, double, false, graph, integer, lower, print, punct, space, true, upper, wordchar, xdigit

However, it is not able to tell me if my variable can be an array, list, or dictionary. Does anyone know how to determine if a variable is one of these three?

Thanks in advance

+7
source share
5 answers

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 ...

+11
source

Other answers to all provide very useful information, but it is worth noting something that many people do not seem to drop in first.

In Tcl, values ​​have no type ... they ask if they can be used as the given type. You can think of it this way.

 string is integer $a 

You don't ask

Is the value in $ a an integer

What are you asking,

Is it possible to use the value in $ a as an integer

It is useful to consider the difference between the two questions when you think in the lines "this is an integer." Each integer is also a valid list (of one element) ... so it can be used like both string is will return true (like several others for an integer).

+3
source

If you want to deal with JSON, I highly recommend that you read the JSON page on the Tcl wiki: http://wiki.tcl.tk/json .

On this page, I posted a simple function that compiles Tcl values ​​into a JSON string, given the formatting descriptor. I also find the discussion on this page very informative.

+1
source

For arrays you want array exists for dicts you want dict exists

for the list I don’t think there is a built-in way to 8.5 ?, there is this from http://wiki.tcl.tk/440

 proc isalist {string} { return [expr {0 == [catch {llength $string}]}] } 
0
source

To determine if a variable is an array:

 proc is_array {var} { upvar 1 $var value if {[catch {array names $value} errmsg]} { return 1 } return 0 } # How to use it array set ar {} set x {1 2 3} puts "ar is array? [is_array ar]"; # ar is array? 1 puts "x is array? [is_array x]"; # x is array? 0 
0
source

All Articles