# this code creates 3 namespaces: {win32}, {win32 media} and {string}
proc {win32 ping} {host} {
puts "Sending ping to host $host"
}
proc {win32} {args} {
set namespace [lindex [info level 0] 0]
if { [llength $args]==0 } {
puts "[info procs [concat $namespace *]]"
} else {
[concat $namespace [lindex $args 0]] {*}[lrange $args 1 end]
}
}
proc {win32 media play} {song} {
puts "playing song $song"
}
proc {win32 media beep} {length} {
puts "beeping for $length"
}
proc {win32 media} {args} {
set namespace [lindex [info level 0] 0]
if { [llength $args]==0 } {
puts "[info procs [concat $namespace *]]"
} else {
"$namespace [lindex $args 0]" {*}[lrange $args 1 end]
}
}
# do some tests with string
rename string _string
proc {string bytelength} {args} {
_string bytelength {*}$args
}
proc {string compare} {args} {
_string compare {*}$args
}
proc {string equal} {args} {
_string equal {*}$args
}
proc {string first} {args} {
_string first {*}$args
}
proc {string index} {args} {
_string index {*}$args
}
proc {string is} {args} {
_string is {*}$args
}
proc {string last} {args} {
_string last {*}$args
}
proc {string length} {string} {
_string length $string
}
proc {string map} {args} {
_string map {*}$args
}
proc {string match} {args} {
_string match {*}$args
}
#range repeat replace tolower toupper totitle trim trimleft trimright wordend wordstart
proc {string} {args} {
# puts "called string $args"
set namespace [lindex [info level 0] 0]
if { [llength $args]==0 } {
puts "[info procs [concat $namespace *]]"
} else {
"$namespace [lindex $args 0]" {*}[lrange $args 1 end]
}
}
puts [string length 12]
# show all commands in the win32 namespace
puts [win32]NEM: See also namespace ensemble command (added in 8.5) that layers similar functionality over existing namespaces. The main differences are that procs in an ensemble are created using traditional
proc foo::bar ...syntax rather than the
proc {foo bar} ...use above. Also calling a namespace ensemble with no arguments produces an error, rather than a list of commands in that namespace. I quite like the idea of producing a list of commands (perhaps it can be done with one of the namespace ensemble options?). Note that you could also extend the syntax of proc in a backwards compatible way to support the following: proc foo bar {args} { ... }This would then naturally extend to creating lambdas: set myfunc [proc {a b} { expr {$a + $b} }]MJ - Note that after some consideration executing a command to determine its subcommands/namespace doesn't seem like too bright of an idea, because you don't know upfront if there are any subcommands and therefore you might actually execute a command that doesn't need any parameters instead of a namespace. Imagine the surprise if an IDE gets the bright idea to find subcommands for the command {system hardisk format}. The way to resolve this is of course to only execute namespaces. Which quickly leads to subcommands that displays procs or namespaces and don't have any side-effects (like executing a command).escargo - I have become more fond of object systems where you can query an object to determine what methods it understands. This is just basic introspection. One of the early suggestions I made to Snit was that types and methods should be able to respond to info commands. In that view, a command should be able to respond to info commands (or info methods) with the appropriate responses.NEM - Yes, an info subcommand is also a good way to go. Conventions for such introspection could be formalised with, for instance Peter DeRijk's interface proposal.MJ - Below a more advanced version that offers other ways of introspection to prevent the problem I mentioned above. I agree with escargo that this is a first start in an OO system with introspection a la Ruby. I do like the easy way you can create nested namespaces though. This is done by redefining proc, which allows you to do something like:
proc { math arithmetic add } { x y } { expr {$x + $y}}This will create all intermediate namespaces and define the proc. proc {ns create} {namespace} {
variable ns
dict set ns $namespace 1
set create_proc [list proc $namespace {args}]
set create_proc [concat $create_proc { {
set namespace [lindex [info level 0] 0]
if { [llength $args]==0 } {
return [concat [ns procs $namespace] [ns children $namespace]]
} else {
"$namespace [lindex $args 0]" {*}[lrange $args 1 end]
}
}}
]
eval $create_proc
}
proc {ns exists} {namespace} {
if {[info proc $namespace]==""} {
return false
} else {
return true
}
}
# create the ns and root namespaces
{ns create} ns
{ns create} {}
proc {ns instproc} {namespace name args body} {
eval [list proc [concat $namespace $name] $args $body]
}
proc {ns instvar} {namespace name value} {
set var_name [concat $namespace $name]
variable $var_name
set $var_name $value
}
proc {ns procs} {namespace} {
set ns_procs ""
foreach ns_proc [info commands [concat $namespace *]] {
if {([llength $ns_proc] == [llength $namespace] + 1) } {
if {[ns is_namespace? $ns_proc ]} continue
lappend ns_procs $ns_proc
}
}
return $ns_procs
}
proc {ns vars} {namespace} {
set ns_vars ""
foreach ns_var [uplevel #0 "info vars {[concat $namespace *]}"] {
if {([llength $ns_var] == [llength $namespace] + 1) } {
lappend ns_vars $ns_var
}
}
return $ns_vars
}
proc {ns is_namespace?} {namespace} {
variable ns
dict exists $ns $namespace
}
proc {ns children} {namespace} {
set ns_children ""
foreach ns_child [info procs [concat $namespace *]] {
if {([llength $ns_child] == [llength $namespace] + 1) } {
if {![ns is_namespace? $ns_child]} continue
lappend ns_children $ns_child
}
}
return $ns_children
}
proc {ns parent} {namespace} {
lrange $namespace 0 end-1
}
# scaffolding is in place
# rename proc so that proc {a b c} {x y z} { ... }
# will create ns a {a b} and define c in {a b} as described in [1]
rename proc _proc
_proc proc {name args body} {
set name [string trim $name]
set namespace [lrange $name 0 end-1]
if {![ns exists $namespace] } {
# warning if the root namespace {} doesn't exist this will recurse infinitely
# puts "creating namespace $namespace"
ns create $namespace
}
_proc $name $args $body
} # example use twapi wrapped in subnamespaces
proc {ns use_with_twapi} {} {
package require twapi
# system functions
proc {twapi system abort_system_shutdown} {args} {
::twapi::abort_system_shutdown {*}$args
}
foreach proc {get_computer_netbios_name get_active_processor_mask} {
proc [list twapi system $proc] {} ::twapi::$proc
}
# sound functions
proc {twapi sound beep} {args} {
::twapi::beep {*}$args
}
}
# display all procs in the global namespace
puts [{}]
# display namespaces under the global namespace
puts [ns children {}]
# if you have twapi
ns use_with_twapi
puts [ns children {}]
# call a twapi command with the new syntax
twapi sound beep -type ok
