Brian Theado 04Aug03 - Thingy: a one-liner OO system implements an object system that reminds me of XOTcl objects. Here I take a stab at adding classes to thingy. Functionality is shared between the various objects and classes by using the namespace import functionality.
XOTcl has two main commands: Object and Class. The Object command creates objects and the Class command creates object creator commands. These object creator commands can have methods (instprocs) attached to them.
XOTcl has a wealth of features few of which are implemented here. No inheritance, no filters, no mixins, etc.
Thingy: a one-liner OO system inspires the following function that behaves very similar to XOTcl's Object command.
proc createPrimitiveObject name { set name [string map {:::: ::} [uplevel 1 namespace current]::$name] proc $name args "namespace eval $name \$args" return $name } # The procedures in the objectmethods namespace will be inherited by all objects namespace eval objectmethods {}
Procedures always execute within the namespace in which they are defined even when they are imported into other namespaces. In order to find which namespace the procedure was actually called from, a combination of uplevel and namespace which can do the trick. Thanks to Mark G. Saye on c.l.t [L1 ] for this tip.
proc objectmethods::self {} { # Case 1: namespace eval Object --> somemethod --> self set proc [lindex [info level -1] 0] set level 2 if {$proc == "namespace"} { # Case2: namespace eval Object --> self incr level -1 set proc [lindex [info level 0] 0] } return [namespace qualifiers [uplevel $level [list namespace which -command $proc]]] } proc objectmethods::destroy {} { set self [self] namespace delete $self rename $self "" } namespace eval objectmethods {namespace export *} # The procedures in classmethods will be inherited by all class objects and should include all the object procedures as well namespace eval classmethods "namespace import [namespace current]::objectmethods::self" proc classmethods::create {name} { set name [uplevel 2 createPrimitiveObject $name] namespace eval [self]::instprocs {} ;# Make sure the namespace exists $name namespace import [self]::instprocs::* return $name } proc classmethods::instproc {name arglist body} { namespace eval [self]::instprocs {} ;# Make sure the namespace exists proc [self]::instprocs::$name $arglist $body namespace eval [self]::instprocs "namespace export $name" } namespace eval classmethods {namespace export *}
An object is-a class
The child namespace "instproc" is used to store those methods that will be inherited by instances
createPrimitiveObject Object namespace eval Object " namespace import [namespace current]::objectmethods::* namespace import -force [namespace current]::classmethods::* " namespace eval Object::instprocs " namespace import [namespace current]::objectmethods::* namespace export * "
createPrimitiveObject Class namespace eval Class " namespace import [namespace current]::objectmethods::* namespace import -force [namespace current]::classmethods::* " namespace eval Class::instprocs " namespace import [namespace current]::objectmethods::* namespace import -force [namespace current]::classmethods::* namespace export * "
Create for the Class object is slightly different--it must also propagate the object methods to the created class's instproc namespace, so override it's definition here.
Class proc create name { set newClass [uplevel Object create $name] namespace eval $newClass { namespace import -force ::classmethods::* } namespace eval ${newClass}::instprocs { namespace import ::objectmethods::* namespace export * } return $newClass }
Features that would be nice to have:
Class create Bagel Bagel create abagel abagel self abagel set toasted 0 abagel info vars info vars abagel::* abagel set toasted abagel destroy Bagel instproc toast {} { [self] incr toasted if {[[self] set toasted] > 1} { error "something's burning!" } return } Bagel create abagel abagel set toasted 0 abagel toast abagel toast
See also another minimal Tcl object system (XOTcl like syntax)