'''eos''': ensemble object system ([neo] is a much better name, [RS] was faster ...) This system is closely related to [neo], and inspired by [NEM]'s '[Using namespace ensemble without a namespace]'. ---- '''eos''' is a slot based system. It provides ''cloning''. ''To be continued ...'' namespace eval eos { variable nobjs0 namespace export * # ::eos:: Create a new object proc {} args { if {[set len [llength $args]] > 1} { set cmd [lindex [info level 1] 0] return -code error "wrong # args: should be \"$cmd ?objname?\"" } if {$len == 1} { set name [lindex $args 0] if {[string range $name 0 1] ne "::"} { set ns [uplevel 1 [list namespace current]] if {![llength $ns]} {set ns ::} set name $ns$name } } else { variable nobjs set name ::eos::OBJ[incr nobjs] } uplevel \#0 [list namespace ensemble create \ -command $name \ -map {} \ -unknown ::eos::unknown\ ] return $name } proc const v {return $v} # The proc unknown defines the default slots, ie, the "class" of the # object. It processes all not-found instances, as it is the -unknown # option in the ensemble as set above. # A new "class" just requires defining a new unknown processor, and setting # it as the -unknown processor in the ensemble. proc unknown {obj cmd args} {list ::eos::*$cmd $obj} # # Define the default functions for the "class": by convention, the names # start with '*'. These are found by the utility function "unknown" above # proc *config {self {new {}}} { set map [namespace ensemble configure $self -map] if {$new eq {}} { return $map } namespace ensemble configure $self -map [dict merge $map $new] } proc *slot {self slot args} { set map [namespace ensemble configure $self -map] dict set map $slot $args namespace ensemble configure $self -map $map } proc *method {self name params body {ns ::}} { set params [linsert $params 0 self] *slot $self $name ::apply [list $params $body $ns] $self } proc *value {self slot args} { if {[llength $args]} { if {[llength $args] != 1} { return -code error "wrong # args: should be \"$self value ?value?\"" } *slot $self $slot ::eos::const [lindex $args 0] return [lindex $args 0] } } proc *clone {self args} { # Assumes that $self only appears at the end (as in methods) if {[llength $args] > 1} { return -code error "wrong # args: should be \"$self clone ?cloneName?\"" } set new [uplevel 1 [list ::eos:: {expand}$args]] set conf [namespace ensemble configure $self] set conf [dict remove $conf -namespace] dict for {slot meth} [dict get $conf -map] { if {[lindex $meth end] eq $self} { dict set conf -map $slot [lreplace $meth end end $new] } } namespace ensemble configure $new {expand}$conf return $new } proc *delete {self} { uplevel 1 [rename $self {}] } } Note that delegation is easy (to other '''eos''' objects, or actually to any other command or object): it suffices to define the default method proc ::eos::*delegate {source method target {tmethod -}} { set target [uplevel 1 [namespace which -command $target]] if {$tmethod eq "-"} { set tmethod $method } *slot $source $method $target $tmethod } However, this is properly the field for an extension of the system: delegation should be combined with proper lifetime management of sub-object created for the purpose of delegation.