• a typeof command for debugging?

    From Mark Summerfield@m.n.summerfield@gmail.com to comp.lang.tcl on Sat Jun 21 11:16:18 2025
    From Newsgroup: comp.lang.tcl

    I'd like a `typeof` command for debugging.
    I've had a go but only bits of it work.

    ```
    proc typeof x {
    if {![catch {[info object class $x] name}]} {
    return $name
    } else {
    if {[string is boolean -strict $x]} {
    return bool
    }
    if {[string is integer -strict $x]} {
    return int
    }
    if {[string is double -strict $x]} {
    return real
    }
    if {[string is dict -strict $x]} {
    return dict
    }
    if {[string is list -strict $x]} {
    return list
    }
    return str
    }
    }

    ::oo::class create Range {
    variable max
    variable current

    constructor max_ { my reset $max_ }

    method reset {{max_ -1}} {
    if {$max_ != -1} { set max $max_ }
    set current -1
    }

    method next varName {
    upvar $varName var
    incr current
    set var $current
    expr { $var < $max }
    }
    }

    set i 55
    set x 9.9
    set b true
    set L {1 2 4 8}
    set d [dict create a 1 b 2 c 4]
    set s "red\therrings"
    set t "long\telephants €"
    set r [Range new 5]

    puts "i=$i typeof=[typeof $i]"
    puts "x=$x typeof=[typeof $x]"
    puts "b=$b typeof=[typeof $b]"
    puts "L=$L typeof=[typeof $L]"
    puts "d=$d typeof=[typeof $d]"
    puts "s=$s typeof=[typeof $s]"
    puts "t=$t typeof=[typeof $t]"
    puts "r=$r typeof=[typeof $r]"
    puts "r=$r info object class=[info object class $r]"

    ```

    Here's the output:

    ```

    i=55 typeof=int
    x=9.9 typeof=real
    b=true typeof=bool
    L=1 2 4 8 typeof=dict
    d=a 1 b 2 c 4 typeof=dict
    s=red herrings typeof=dict
    t=long elephants € typeof=list
    r=::oo::Obj24 typeof=list
    r=::oo::Obj24 info object class=::Range

    ```
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Christian Gollwitzer@auriocus@gmx.de to comp.lang.tcl on Sat Jun 21 15:51:31 2025
    From Newsgroup: comp.lang.tcl

    Am 21.06.25 um 13:16 schrieb Mark Summerfield:
    I'd like a `typeof` command for debugging.
    I've had a go but only bits of it work.

    ```
    proc typeof x {
    if {![catch {[info object class $x] name}]} {
    return $name
    } else {
    if {[string is boolean -strict $x]} {
    return bool
    [...]

    I don't think you can do much better than that, since Tcl is a weakly
    typed language (usually referred to as the "EIAS" principle). The only
    other thing you can do is peek into the internal cached type, which
    shows you the last type that was used on that object (besides string):

    (chris) 50 % set a [expr {23*5}]
    115
    (chris) 51 % tcl::unsupported::representation $a
    value is a int with a refcount of 4, object pointer at 0x55d6b8f8b320, internal representation 0x73:(nil), string representation "115"


    Christian
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Christian Gollwitzer@auriocus@gmx.de to comp.lang.tcl on Sat Jun 21 15:58:50 2025
    From Newsgroup: comp.lang.tcl

    shows you the last type that was used on that object (besides string):

    (chris) 50 % set a [expr {23*5}]
    115
    (chris) 51 % tcl::unsupported::representation $a
    value is a int with a refcount of 4, object pointer at 0x55d6b8f8b320, internal representation 0x73:(nil), string representation "115"


             Christian

    PS: Since you are new to Tcl, please have a look at the following:

    (chris) 52 % set a {1 2 3 4}
    1 2 3 4
    (chris) 53 % tcl::unsupported::representation $a
    value is a pure string with a refcount of 5, object pointer at
    0x55d6b8f8ac90, string representation "1 2 3 4"
    (chris) 54 % lindex $a 2
    3
    (chris) 55 % tcl::unsupported::representation $a
    value is a list with a refcount of 4, object pointer at 0x55d6b8f8ac90, internal representation 0x55d6b8c06cf0:(nil), string representation "1 2
    3 4"
    (chris) 56 %

    Also, the {} do not make the list, they are a quoting mechanism. You
    will get an identical result if you use "":
    (chris) 61 % tcl::unsupported::representation $b
    value is a pure string with a refcount of 5, object pointer at
    0x55d6b8f85860, string representation "1 2 3 4 5 6"
    (chris) 62 % lindex $b 2
    3
    (chris) 63 % tcl::unsupported::representation $b
    value is a list with a refcount of 4, object pointer at 0x55d6b8f85860, internal representation 0x55d6b8f14b40:(nil), string representation "1 2
    3 4 5 6"
    (chris) 64 %


    And, there is "constant sharing". After you ran the previous examples,
    try with another surprise:

    (chris) 64 % set c "1 2 3 4"
    1 2 3 4
    (chris) 65 % tcl::unsupported::representation $c
    value is a list with a refcount of 9, object pointer at 0x55d6b8f8ac90, internal representation 0x55d6b8c06cf0:(nil), string representation "1 2
    3 4"
    (chris) 66 %


    Christian
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Mark Summerfield@m.n.summerfield@gmail.com to comp.lang.tcl on Sun Jun 22 06:50:14 2025
    From Newsgroup: comp.lang.tcl

    On Sat, 21 Jun 2025 15:51:31 +0200, Christian Gollwitzer wrote:

    Am 21.06.25 um 13:16 schrieb Mark Summerfield:
    I'd like a `typeof` command for debugging.
    I've had a go but only bits of it work.

    ```
    proc typeof x {
    if {![catch {[info object class $x] name}]} {
    return $name
    } else {
    if {[string is boolean -strict $x]} {
    return bool
    [...]

    I don't think you can do much better than that, since Tcl is a weakly
    typed language (usually referred to as the "EIAS" principle). The only
    other thing you can do is peek into the internal cached type, which
    shows you the last type that was used on that object (besides string):

    (chris) 50 % set a [expr {23*5}]
    115 (chris) 51 % tcl::unsupported::representation $a value is a int with
    a refcount of 4, object pointer at 0x55d6b8f8b320,
    internal representation 0x73:(nil), string representation "115"


    Christian

    I found that tcl::unsupported::representation says "pure string" for
    strings and numbers and bools and lists; it only seems to distinguish
    dicts (Tcl 9.0.1).

    I've incorporated that but I still have an actual bug. The catch always returns false (failed) so objects always get returned as list_or_str. Yet,
    if I use info object class directly it correctly returns the class name.
    So clearly I'm doing something wrong in the catch.

    proc typeof x {
    # puts "\n[tcl::unsupported::representation $x]"
    if {![catch {[info object class $x] name}]} {
    return $name
    } else {
    if {[string is boolean -strict $x]} { return bool }
    if {[string is integer -strict $x]} { return int }
    if {[string is double -strict $x]} { return real }
    if {[string match "value is a dict*" \
    [tcl::unsupported::representation $x]]} {
    return dict
    }
    if {[string is list -strict $x]} { return list_or_str }
    return str
    }
    }

    In the above the catch always returns false.

    set r [Range new 5]
    puts "r=$r typeof=[typeof $r]"
    puts "r=$r info object class=[info object class $r]"

    Outputs:

    r=::oo::Obj24 typeof=list_or_str
    r=::oo::Obj24 info object class=::Range
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Ralf Fassel@ralfixx@gmx.de to comp.lang.tcl on Sun Jun 22 13:08:53 2025
    From Newsgroup: comp.lang.tcl

    * Mark Summerfield <m.n.summerfield@gmail.com>
    | I've incorporated that but I still have an actual bug. The catch always
    | returns false (failed) so objects always get returned as list_or_str. Yet,
    | if I use info object class directly it correctly returns the class name.
    | So clearly I'm doing something wrong in the catch.

    | proc typeof x {
    | # puts "\n[tcl::unsupported::representation $x]"
    | if {![catch {[info object class $x] name}]} {
    | return $name

    This looks suspicious: you are calling
    [info object class $x]
    and whatever that returns, you use as a command to call with argument 'name', and discard the result. The variable $name should not be set when you hit
    the return.

    Did you rather mean

    catch {info object class $x} name

    ?
    R'
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Mark Summerfield@m.n.summerfield@gmail.com to comp.lang.tcl on Sun Jun 22 11:42:53 2025
    From Newsgroup: comp.lang.tcl

    On Sun, 22 Jun 2025 13:08:53 +0200, Ralf Fassel wrote:

    * Mark Summerfield <m.n.summerfield@gmail.com>
    | I've incorporated that but I still have an actual bug. The catch
    always | returns false (failed) so objects always get returned as list_or_str. Yet, | if I use info object class directly it correctly
    returns the class name.
    | So clearly I'm doing something wrong in the catch.

    | proc typeof x {
    | # puts "\n[tcl::unsupported::representation $x]"
    | if {![catch {[info object class $x] name}]} {
    | return $name

    This looks suspicious: you are calling
    [info object class $x]
    and whatever that returns, you use as a command to call with argument
    'name',
    and discard the result. The variable $name should not be set when you
    hit the return.

    Did you rather mean

    catch {info object class $x} name

    ?
    R'

    Thank you, you are right. (I haven't got up to catch in the 2nd edition of Ashok's book yet.)

    With your correction I now get class names:

    proc typeof x {
    # puts "\n[tcl::unsupported::representation $x]"
    if {![catch {info object class $x} name]} {
    return $name
    } else {
    if {[string is boolean -strict $x]} { return bool }
    if {[string is integer -strict $x]} { return int }
    if {[string is double -strict $x]} { return real }
    if {[string match "value is a dict*" \
    [tcl::unsupported::representation $x]]} {
    return dict
    }
    if {[string is list -strict $x]} { return list_or_str }
    return str
    }
    }

    set i 55
    set j 0d55
    set x 9.9
    set b true
    set L {1 2 4 8}
    set d [dict create a 1 b 2 c 4]
    set s "red\therrings"
    set t "long\telephants €"
    set r [Range new 5]

    puts "i=$i typeof=[typeof $i]"
    puts "j=$j typeof=[typeof $j]"
    puts "x=$x typeof=[typeof $x]"
    puts "b=$b typeof=[typeof $b]"
    puts "L=$L typeof=[typeof $L]"
    puts "d=$d typeof=[typeof $d]"
    puts "s=$s typeof=[typeof $s]"
    puts "t=$t typeof=[typeof $t]"
    puts "r=$r typeof=[typeof $r]"

    Output:

    i=55 typeof=int
    j=0d55 typeof=int
    x=9.9 typeof=real
    b=true typeof=bool
    L=1 2 4 8 typeof=list_or_str
    d=a 1 b 2 c 4 typeof=dict
    s=red herrings typeof=list_or_str
    t=long elephants € typeof=list_or_str
    r=::oo::Obj24 typeof=::Range
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Christian Gollwitzer@auriocus@gmx.de to comp.lang.tcl on Sun Jun 22 21:22:37 2025
    From Newsgroup: comp.lang.tcl

    Am 22.06.25 um 08:50 schrieb Mark Summerfield:
    I found that tcl::unsupported::representation says "pure string" for
    strings and numbers and bools and lists; it only seems to distinguish
    dicts (Tcl 9.0.1).


    No, it doesn't. You seem to have the misconception that {1 2 3} is a
    list of three values. It is actually a string, but can be treated as a list.

    In Tcl, neither the values nor the variables do have a type in the sense
    of Python. However, the runtime uses an internal cached typed
    representation, which you can query.

    See this transcript:

    (chris) 49 % set a [list 1 2 3]
    1 2 3
    (chris) 50 % tcl::unsupported::representation $a
    value is a list with a refcount of 4, object pointer at 0x5576f6d87430, internal representation 0x5576f6d57ab0:(nil), string representation "1 2 3" (chris) 51 % set b [dict a 1 b 2]
    b 2
    (chris) 52 % tcl::unsupported::representation $b
    value is a dict with a refcount of 4, object pointer at 0x5576f6d86e30, internal representation 0x5576f6da2d60:(nil), string representation "b 2" (chris) 53 % set c [expr {1.0/3}]
    0.3333333333333333
    (chris) 54 % tcl::unsupported::representation $c
    value is a double with a refcount of 4, object pointer at
    0x5576f6d86cb0, internal representation 0x3fd5555555555555:(nil), string representation "0.33333333333..."
    (chris) 55 % set d yes; if {$d} { puts yes }
    yes
    (chris) 56 % tcl::unsupported::representation $d
    value is a booleanString with a refcount of 4, object pointer at 0x5576f6d880f0, internal representation 0x1:0x5576f6d815e0, string representation "yes"
    (chris) 57 %

    Read up on EIAS if you struggle to understand this. It is alos the
    reason why this is in the "unsupported" namespace, because this command
    breaks EIAS.

    Christian


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Schelte@nospam@wanadoo.nl to comp.lang.tcl on Sun Jun 22 23:50:48 2025
    From Newsgroup: comp.lang.tcl

    On 22/06/2025 21:22, Christian Gollwitzer wrote:
    (chris) 51 % set b [dict a 1 b 2]
    b 2
    (chris) 52 % tcl::unsupported::representation $b
    value is a dict with a refcount of 4, object pointer at 0x5576f6d86e30, internal representation 0x5576f6da2d60:(nil), string representation "b 2"

    Funny. While clearing up one point, you are confusing readers again on
    another point: It may not immediately be clear how b can end up being
    the dictionary "b 2". The explanation is that the command [dict a 1 b 2]
    is interpreted as [dict append 1 b 2], because there is only one dict subcommand that starts with "a". Additionally "dict append" will create
    the dict if it doesn't yet exist. So this creates a dict in a variable
    called "1", with key "b" that has a value of "2". That dict is then also copied to the variable "b".

    I suppose you actually intended to use [dict create a 1 b 2].


    Schelte.

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Christian Gollwitzer@auriocus@gmx.de to comp.lang.tcl on Mon Jun 23 08:06:48 2025
    From Newsgroup: comp.lang.tcl

    Am 22.06.25 um 23:50 schrieb Schelte:
    On 22/06/2025 21:22, Christian Gollwitzer wrote:
    (chris) 51 % set b [dict a 1 b 2]
    b 2
    (chris) 52 % tcl::unsupported::representation $b
    value is a dict with a refcount of 4, object pointer at
    0x5576f6d86e30, internal representation 0x5576f6da2d60:(nil), string
    representation "b 2"

    Funny. While clearing up one point, you are confusing readers again on another point: It may not immediately be clear how b can end up being
    the dictionary "b 2". The explanation is that the command [dict a 1 b 2]
    is interpreted as [dict append 1 b 2], because there is only one dict subcommand that starts with "a". Additionally "dict append" will create
    the dict if it doesn't yet exist. So this creates a dict in a variable called "1", with key "b" that has a value of "2". That dict is then also copied to the variable "b".

    I suppose you actually intended to use [dict create a 1 b 2].


    Good catch! I missed that one.

    Christian
    --- Synchronet 3.21a-Linux NewsLink 1.2