typedlist

FM Using TclOO to create "typed lists"

oo::class create typedlist {
    constructor {args} {
        my variable Liste
        my variable Type
        my variable counter -1
        if {[lindex $args 1] eq "-namespace"} {
            set namespace [lindex $args 2]
            uplevel [list namespace eval $namespace {}]
            set List [lindex $args 0]
        } else {
            set namespace []
            set Liste $args
        }
        set i [string last : [namespace tail [self]]]
        set Type [string range [namespace tail [self]] 0 $i-1]
        oo::objdefine [self] [subst {
            method unknown {args} {
                my variable counter
                typedlist create [list [self]]:\[incr counter\] {*}\$args
            }
        }]
    }
    method append {index args} {
        my variable Liste
        set e [lindex $Liste $index]
        lset Liste $index [append e {*}$args]
    }
    method get {} {
        my variable Liste
        return $Liste
    }
    method lappend {args} {
        my variable Liste
        lappend Liste {*}$args
    }
    method replace {debut fin args} {
        my variable Liste
        typedlist::nettoyer $Liste $debut $fin
        return [set Liste [lreplace $Liste $debut $fin {*}$args]]
    }
    method search {args} {
        my variable Liste
        set pattern [lindex $args end]
        return [lsearch {*}[lrange $args 0 end-1] $Liste $pattern]
    }
    method type {} {
        my variable Type
        return $Type
    }
    method transmute {type} {
        my variable Liste
        if {[info object class $type] eq "::typedlist"} {
            set new [$type -- {*}$Liste]
            after idle [list [self] destroy]
            return $new
        }
    }
    method -- {args} {
        my variable counter
        typedlist create [self]:[incr counter] {*}$args
    }

    export --

}

# Some additionals tools

namespace eval typedlist {}

# recreate a script from a typedlist

proc ::typedlist::scriptifier {Typed} {
    set L []
    set T []
    if {[info object isa object $Typed] && [info object class $Typed] eq "::typedlist"} {
        foreach e [$Typed get] {
            append L \  [::typedlist::scriptifier $e]
        }
        append T [$Typed type] \  -- \ 
    } else {
        set L \{$Typed\}
        set T []
        return ${T}${L}
    }
    return \[$T$L\]
}

# test if the object is a typedlist

proc ::typedlist::is {T} {
    if {[info object isa object $T] \
            && [info object class $T] eq "::typedlist"} {
        return 1
    } else {
        return 0
    }
}

# equality test between two typedlist

proc ::typedlist::eq {L1 L2} {
    set res 0
    if {[::typedlist::is $L1] && [::typedlist::is $L2]} {
        if {[$L1 type] eq [$L2 type]} {
            foreach l1 [$L1 get] l2 [$L2 get] {
                set res [::typedlist::eq $l1 $l2]
            }
        }
    } elseif {![::typedlist::is $L1] && ![::typedlist::is $L2]} {
        if {$L1 eq $L2} {
            set res 1
        }
    }
    return $res
}

# clean-up tool

proc ::typedlist::nettoyer {Typed {debut {0}} {fin {end}}} {
    set L [list]
    if {[::typedlist::is $Typed]} {
        foreach e [lrange [$Typed get] $debut $fin] {
            ::typedlist::nettoyer $e
            if {[catch {$e destroy} err]} {
                puts stderr "erreur lors de la destruction de la liste $e, 
procédure : ::typedlist::nettoyer $Typed
message   : $err"
            }
        }
    } 
    return
}
package provide typedlist 0.2

What does it ? An object containing a type and a list.

Quick démo :

typedlist create rectangle
set A [rectangle -- 100 100 200 200]
$A get; # 100 100 200 200
$A type; # rectangle
$A lappend 300 100; # 100 100 200 200 300 100
typedlist create triangle
$A transmute ::triangle
$A type; # triangle
$A get; # 100 100 200 200 300 100

Applications :


PYK 2014-11-06: Made a small change to make sure a value in generated code was properly encasulated in a list, and another small change to handle relative namespaces better.

greg 2024-04-07: With Quick demo the type is not changed: After the transmutation, the new object ($A after the transmutation) must be assigned to the variable A again. This is a crucial step because it updates the reference from the original object to the new object.

typedlist create rectangle
set A [rectangle -- 100 100 200 200]
$A get; # 100 100 200 200
$A type; # rectangle
$A lappend 300 100; # 100 100 200 200 300 100
typedlist create triangle
set A [$A transmute ::triangle]
$A type; # triangle
$A get; # 100 100 200 200 300 100

Also the use of after idle in the transmute method. Can this cause side effects? The after idle list [self destroy] happens at some point. Wouldn't a direct [self] destroy be better?