Identifying variable types in Tcl

Tcl has the ability to transform values from one type to another without making this process apparent[*]. The only way to determine if a value is a certain type is by verifying its conformity to that type. Essentially, in Tcl, a value’s type is not explicit; the concern is whether it is suitable for a specified type.

Question:

I’m in search of a method to determine the data type of a variable in tcl. To clarify, if I have the variable $a, I’m interested in discovering whether it is an integer or not.

Up until now, I have utilized the following.

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

The following types seem to function effectively with this approach: alnum, alpha, ascii, boolean, control, digit, double, false, graph, integer, lower, print, punct, space, true, upper, wordchar, and xdigit.

Is there a way to determine whether a variable is an array, a list, or a dictionary? Although it cannot identify the type of my variable, I am curious if there are any solutions available.


Solution 1:

In Tcl, values have varying types except when they are an associative array of variables, which is indicated by the syntax

$foo(bar)

and requires the use of

array exists

. However, it’s important to note that Tcl values can be mutated between different types without revealing this information. Therefore, the only way to determine a value’s type is to check if it conforms to a specific type.

Conformance validation is performed using

string is

which requires the option

-strict

for historical purposes.

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])"
    }
}

It should be noted that the values are always serializable and comply with the type of
strings; Tcl’s
.

As per the comments, it is feasible to apply unconventional techniques to perform JSON serialization and produce an accurate serialization. Tcl language’s perspective suggests that putting everything in a string would be the right approach, but it may not be very useful for other programming languages. Tcl version 8.6 allows the execution of this code, which was initially published 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 {k v} $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""""

Frequently Asked Questions