define(3tcl)
π
define(3tcl) β TclOO Commands β¨
πΈ NAME πΈ
okay so bestie, this is all about oo::define, oo::objdefine, and oo::Slot β they define and configure classes and objects. periodt. π
β¨ SYNOPSIS β¨
package require tcl::oo
oo::define class defScript
oo::define class subcommand arg ?arg ...?
oo::objdefine object defScript
oo::objdefine object subcommand arg ?arg ...?
oo::Slot arg...
π¦ CLASS HIERARCHY π¦
oo::object
β oo::Slot
π DESCRIPTION π
So the oo::define command is your girl for controlling the configuration of classes, and the oo::objdefine command is for controlling the configuration of objects β including classes as instance objects. The configuration gets applied to the entity named in the class or the object argument. And like, configuring a class also updates the configuration of all subclasses of the class and all objects that are instances of that class or which mix it in (as modified by any per-instance configuration). It's giving cascade energy. π
The way the configuration is done is controlled by either the defScript argument or by the subcommand and following arg arguments. When the second is present, it is exactly as if all the arguments from subcommand onwards are made into a list and that list is used as the defScript argument. No cap.
π‘ Note that the constructor foroo::classwill calloo::defineon thescriptargument that it is provided. This is a convenient way to create and define a class in one step. iconic.
π CONFIGURING CLASSES π
The following commands are supported in the defScript for oo::define, and each of them may also be used in the subcommand form. Let's go through them one by one, bestie. π
classmethod name ?argList bodyScrip?β This creates a class method, or (ifargListandbodyScriptare omitted) promotes an existing method on the class object to be a class method. Thename,argListandbodyScriptarguments are as in the method definition, below.
Class methods can be called on either the class itself or on the instances of that class. When they are called, the current object (see the sel and my commands) is the class on which they are called or the class of the instance on which they are called, depending on whether they are called on the class or an instance of the class, respectively. If called on a subclass or instance of the subclass, the current object is the subclass.
In a private definition context, the methods as invoked on classes are not private, but the methods as invoked on instances of classes are private. π
constructor argList bodyScriptβ This creates or updates the constructor for a class. The formal arguments to the constructor (defined using the same format as for theTcl proccommand) will beargList, and the body of the constructor will bebodyScript. When the body of the constructor is evaluated, the current namespace of the constructor will be a namespace that is unique to the object being constructed. Within the constructor, thenextcommand should be used to call the superclasses' constructors. IfbodyScriptis the empty string, the constructor will be deleted.
Classes do not need to have a constructor defined. If none is specified, the superclass's constructor will be used instead. slay. β¨
destructor bodyScriptβ This creates or updates the destructor for a class. Destructors take no arguments, and the body of the destructor will bebodyScript. The destructor is called when objects of the class are deleted, and when called will have the object's unique namespace as the current namespace. Destructors should use thenextcommand to call the superclasses' destructors. Note that destructors are not called in all situations (e.g. if the interpreter is destroyed). IfbodyScriptis the empty string, the destructor will be deleted.
π Note that errors during the evaluation of a destructor are not returned to the code that causes the destruction of an object. Instead, they are passed to the currently-defined bgerror handler.
export name ?name ...?β This arranges for each of the named methods,name, to be exported (i.e. usable outside an instance through the instance object's command) by the class being defined. Note that the methods themselves may be actually defined by a superclass; subclass exports override superclass visibility, and may in turn be overridden by instances. πforward name cmdName ?arg ...?β This creates or updates a forwarded method calledname. The method is defined be forwarded to the command calledcmdName, with additional arguments,argetc., added before those arguments specified by the caller of the method. ThecmdNamewill always be resolved using the rules of the invoking objects' namespaces, i.e., whencmdNameis not fully-qualified, the command will be searched for in each object's namespace, using the instances' namespace's path, or by looking in the global namespace. The method will be exported ifnamestarts with a lower-case letter, and non-exported otherwise.
If in a private definition context (see the private definition command, below), this command creates private forwarded methods. π
initialise script/initialize scriptβ This evaluatesscriptin a context which supports local variables and where the current namespace is the instance namespace of the class object itself. This is useful for setting up, e.g., class-scoped variables. π§method name ?option? argList bodyScriptβ This creates or updates a method that is implemented as a procedure-like script. The name of the method isname, the formal arguments to the method (defined using the same format as for theTcl proccommand) will beargList, and the body of the method will bebodyScript. When the body of the method is evaluated, the current namespace of the method will be a namespace that is unique to the current object. The method will be exported ifnamestarts with a lower-case letter, and non-exported otherwise; this behavior can be overridden viaexportandunexportor by specifying-export,-privateor-unexportin the optional parameteroption.
If in a private definition context (see the private definition command, below) or if the -private flag is given for option, this command creates private procedure-like methods. π
private cmd arg.../private scriptβ This evaluates thescript(or the list of command and arguments given bycmdandargs) in a context where the definitions made on the current class will be private definitions.
The following class definition commands are affected by private: forward, method, self, and variable. Nesting private inside private has no cumulative effect; the innermost definition context is just a private definition context. All other definition commands have no difference in behavior when used in a private definition context. π
self subcommand arg .../self script/selfβ This command is equivalent to callingoo::objdefineon the class being defined (see CONFIGURING OBJECTS below for a description of the supported values ofsubcommand). It follows the same general pattern of argument handling as theoo::defineandoo::objdefinecommands, and βoo::define cls self subcommand ...β operates identically to βoo::objdefine cls subcommand ...β.
If no arguments at all are used, this gives the name of the class currently being configured. If in a private definition context (see the private definition command, below), the definitions on the class object will also be made in a private definition context. π
superclass ?-slotOperation? ?className ...?β This slot (see SLOTTED DEFINITIONS below) allows the alteration of the superclasses of the class being defined. EachclassNameargument names one class that is to be a superclass of the defined class. Note that objects must not be changed from being classes to being non-classes or vice-versa, that an empty parent class is equivalent tooo::object, and that the parent classes ofoo::objectandoo::classmay not be modified. By default, this slot works by replacement. πunexport name ?name ...?β This arranges for each of the named methods,name, to be not exported (i.e. not usable outside the instance through the instance object's command, but instead just through themycommand visible in each object's context) by the class being defined. Note that the methods themselves may be actually defined by a superclass; subclass unexports override superclass visibility, and may be overridden by instance unexports. πvariable ?-slotOperation? ?name ...?β This slot (see SLOTTED DEFINITIONS below) arranges for each of the named variables to be automatically made available in the methods, constructor and destructor declared by the class being defined. Each variable name must not have any namespace separators and must not look like an array access. All variables will be actually present in the namespace of the instance object on which the method is executed. Note that the variable lists declared by a superclass or subclass are completely disjoint, as are variable lists declared by instances; the list of variable names is just for methods (and constructors and destructors) declared by this class. By default, this slot works by appending. π
If you're in a private definition context (peep the private definition command, it's coming below π), this slot is manipulating the list of private variable bindings for this class. So here's the tea on a private variable binding: the name the variable has inside the instance object is not the same as the name you gave in the definition. The name you used in the definition? That's your key to reaching the variable from the methods of this class ποΈ. And the name in the instance namespace gets a unique prefix, which makes it extremely unlikely that some other class accidentally touches it. Love that for us π
.
π ADVANCED CLASS CONFIGURATION OPTIONS π
Okay bestie, these next definitions are 100% supported, they're just not required if your program is doing simple things. No pressure, but they're iconic:
definitionnamespace ?kind? namespaceNameβ this one gives you control over which namespace theoo::defineandoo::objdefinecommands use to look up the definition commands they run. Whenever any object has a definition operation applied to it, the class that it's an instance of (plus its superclasses and mixins) gets consulted about which definition namespace to use.oo::definegrabs the class definition namespace, and::oo::objdefinegrabs the instance definition namespace, but other than that they use the exact same lookup operation. β¨
So what this command does: it sets the definition namespace of kind kind provided by the current class to namespaceName. That namespaceName has to point at a namespace that actually exists right now, or it can be the empty string (which is the way to stop the current class from having such a namespace connected at all). The kind, if you bother supplying it, must be either -class (that's the default) or -instance, and it tells you whether you're setting the namespace used with oo::define or the one used with oo::objdefine respectively.
Important lore: the class oo::object has its instance namespace locked to ::oo::objdefine, and the class oo::class has its class namespace locked to ::oo::define. And a consequence of that is if you want to actually use this feature effectively for classes, you're gonna need to define a metaclass. Periodt.
deletemethod name ?name ...?β deletes each of the methods called name from a class. Those methods must have already existed in that class, no sneaky deletions of things that were never there. It does not touch the superclasses of the class, and it doesn't touch the subclasses or instances of the class either β except when they have a call chain running through the class being modified β and it doesn't touch the class object itself. Okay?filter ?-slotOperation? ?methodName ...?β this slot (go read SLOTTED DEFINITIONS below, bestie) sets or updates the list of method names that guard whether a method call to instances of the class is even allowed to be called, and what that method's results are. Each methodName names a single filtering method (it may be exposed or not exposed, either is fine). And it is not an error to name a method that doesn't exist yet, since subclasses might define it later. By default, this slot works by appending. π«Άmixin ?-slotOperation? ?className ...?β another slot (again, SLOTTED DEFINITIONS, scroll down πΈ). This one sets or updates the list of extra classes that get mixed into all the instances of the class you're defining. Each className argument names one single class that's getting mixed in. By default, this slot works by replacement.renamemethod fromName toNameβ renames the method called fromName in a class to toName. The method has to have already existed in the class, and toName must not already refer to some method in that class. It does not affect the superclasses of the class, and it doesn't affect the subclasses or instances of the class β except when they have a call chain through the class being modified β and it doesn't affect the class object itself either. Oh and it does not change the export status of the method: if it was exported before, it stays exported afterwards. No takebacks, no glow-down π .
π¦ CONFIGURING OBJECTS π¦
okay bestie, here's where it gets juicy. these are the commands you can use in the defScript for oo::objdefine, and every single one of them also works in subcommand form. so pick your fave, they're both valid π
export name ?name ...?β this arranges for each of the named methods, name, to be exported (i.e. usable outside the object through the object's command) by the object being defined. important lil detail: those methods might actually be defined by a class or a superclass, and object exports override class visibility. periodt πforward name cmdName ?arg ...?β creates or updates a forwarded object method called name. the method is defined to be forwarded to the command called cmdName, with additional arguments, arg etc., added before those arguments specified by the caller of the method. forwarded methods should be deleted using themethodsubcommand. the method will be exported if name starts with a lower-case letter, and non-exported otherwise.
if in a private definition context (see the private definition command, below), this command creates private forwarded methods. π
method name ?option? argList bodyScriptβ this creates, updates or deletes an object method. the name of the method is name, the formal arguments to the method (defined using the same format as for the Tclproccommand) will be argList, and the body of the method will be bodyScript. when the body of the method is evaluated, the current namespace of the method will be a namespace that is unique to the object. the method will be exported if name starts with a lower-case letter, and non-exported otherwise; this can be overridden by specifying-export,-privateor-unexportin the optional parameter option, or via theexportandunexportdefinitions. β¨
if in a private definition context (see the private definition command, below) or if the -private flag is given for option, this command creates private procedure-like methods.
mixin ?-slotOperation? ?className ...?β this slot (see SLOTTED DEFINITIONS below) sets or updates a per-object list of additional classes that are to be mixed into the object. each argument, className, names a single class that is to be mixed in. by default, this slot works by replacement. πprivate cmd arg...private scriptβ this evaluates the script (or the list of command and arguments given by cmd and args) in a context where the definitions made on the current object will be private definitions.
the following class definition commands are affected by private: forward, method, and variable. nesting private inside private has no cumulative effect; the innermost definition context is just a private definition context. all other definition commands have no difference in behavior when used in a private definition context.
unexport name ?name ...?β this arranges for each of the named methods, name, to be not exported (i.e. not usable outside the object through the object's command, but instead just through themycommand visible in the object's context) by the object being defined. note that the methods themselves may be actually defined by a class; instance unexports override class visibility. π₯variable ?-slotOperation? ?name ...?β this slot (see SLOTTED DEFINITIONS below) arranges for each of the named variables to be automatically made available in the methods declared by the object being defined. each variable name must not have any namespace separators and must not look like an array access. all variables will be actually present in the namespace of the object on which the method is executed. note that the variable lists declared by the classes and mixins of which the object is an instance are completely disjoint; the list of variable names is just for methods declared by this object. by default, this slot works by appending. πΈ
if in a private definition context (see the private definition command, below), this slot manipulates the list of private variable bindings for this object. in a private variable binding, the name of the variable within the instance object is different to the name given in the definition; the name used in the definition is the name that you use to access the variable within the methods of this instance object, and the name of the variable in the instance namespace has a unique prefix that makes accidental use from superclass methods extremely unlikely. π§
π ADVANCED OBJECT CONFIGURATION OPTIONS π
these definitions are also supported, but they're not required in simple programs. lowkey powerful though, so don't sleep on 'em.
class classNameβ this allows the class of an object to be changed after creation. note that the class's constructors are not called when this is done, and so the object may well be in an inconsistent state unless additional configuration work is done. πdeletemethod name ?name ...β this deletes each of the methods called name from an object. the methods must have previously existed in that object (e.g., because it was created throughoo::objdefine method). does not affect the classes that the object is an instance of, or remove the exposure of those class-provided methods in the instance of that class.filter ?-slotOperation? ?methodName ...?β this slot (see SLOTTED DEFINITIONS below) sets or updates the list of method names that are used to guard whether a method call to the object may be called and what the method's results are. each methodName names a single filtering method (which may be exposed or not exposed); it is not an error for a non-existent method to be named. note that the actual list of filters also depends on the filters set upon any classes that the object is an instance of. by default, this slot works by appending. β¨renamemethod fromName toNameβ this renames the method called fromName in an object to toName. the method must have previously existed in the object, and toName must not previously refer to a method in that object. does not affect the classes that the object is an instance of and cannot rename in an instance object the methods provided by those classes (though anoo::objdefineforwarded method may provide an equivalent capability). does not change the export status of the method; if it was exported before, it will be afterwards. π«Άselfβ this gives the name of the object currently being configured. β
π PRIVATE METHODS π
so when a class or instance has a private method, that private method can only be invoked from within methods of that class or instance. other callers of the object's methods cannot invoke private methods β it's as if the private methods don't exist. it's giving invisible. π»
however, a private method of a class can be invoked from the class's methods when those methods are being used on another instance object. this means that a class can use them to coordinate behaviour between several instances of itself without interfering with how other classes (especially either subclasses or superclasses) interact. iconic, honestly.
private methods precede all mixed in classes in the method call order (as reported by self call). π₯
π SLOTTED DEFINITIONS π
okay bestie sit down because this part is so important β¨
some of the configurable definitions of a class or object are what we call slotted definitions. what that means: the config is actually implemented by a slot object, which is an instance of the class oo::Slot, and that slot object is the one managing a list of values (class names, variable names, etc.) that makes up the contents of the slot. yes, it's an object managing a list for another object. it's giving β¨delegationβ¨.
the oo::Slot class defines six operations (as methods) that you can do on the slot:
slot -append ?member ...?β appends the given member elements to the slot definition π«Άslot -appendifnew ?member ...?β appends the given member elements to the slot definition if they do not already exist (so polite, so demure)slot -clearβ sets the slot definition to the empty list. bye bye π¨slot -prepend ?member ...?β prepends the given member elements to the slot definition. front of the line, main character energy πslot -remove ?member ...?β removes the given member elements from the slot definition ποΈslot -set ?member ...?β replaces the slot definition with the given member elements. full reset, no leftovers.
a consequence of all this (and this is the gotcha bestie, write it down π): any use of a slot's default operation where the first member argument begins with a hyphen will be an error. so in those circumstances you have to use one of the above operations explicitly. like, say, spell it out. no shortcut for you πΈ
also: you only need to make an instance of oo::Slot if you are defining your own slot that behaves like a standard slot. if you're not doing that, you don't need to touch this. periodt.
π SLOT IMPLEMENTATION π
internally (iykyk π), slot objects also define a method called --default-operation which gets forwarded to the default operation of the slot. (so, for the class "variable" slot, this is forwarded to "my -append" π
.) and then there are these methods, which provide the implementation interface:
slot Getβ returns a list that is the current contents of the slot, but does not modify the slot. this method must always be called from a stack frame created by a call tooo::defineoroo::objdefine. it should not return an error unless it is called from outside a definition context or with the wrong number of arguments.
the elements of the list should be fully resolved, if that is a meaningful concept to the slot. β¨
slot Resolve slotElementβ returns slotElement with a resolution operation applied to it, but does not modify the slot. for slots of simple strings, this is an operation that does nothing, whereas for slots of classes, this maps a class name to its fully-qualified class name. this method must always be called from a stack frame created by a call tooo::defineoroo::objdefine. it should not return an error unless it is called from outside a definition context or with the wrong number of arguments; unresolvable arguments should be returned as is (because not all slot operations strictly require that values are resolvable to work π« ).
implementations should not enforce uniqueness and ordering constraints in this method β that is the responsibility of the Set method. a default implementation of this method (that just returns its slotElement argument) is provided in the oo::Slot class. π
slot Set elementListβ sets the contents of the slot to the list elementList and returns the empty string. this method must always be called from a stack frame created by a call tooo::defineoroo::objdefine. this method may return an error if it rejects the change to the slot contents (e.g., because of invalid values), as well as if it is called from outside a definition context or with the wrong number of arguments.
this method may reorder and filter the elements if that's necessary to satisfy the underlying constraints of the slot. (for example: slots of classes enforce a uniqueness constraint that places each element in the earliest location in the slot that it can. so it's like β¨sorted into its rightful spotβ¨.)
the implementation of these methods is slot-dependent (and responsible for accessing the correct part of the class or object definition β that's the whole job bestie π₯). slots also have an unknown method handler to tie all these pieces together, and they hide their destroy method so that it is not invoked inadvertently π. it is recommended that any user changes to the slot mechanism itself be restricted to defining new operations whose names start with a hyphen.
note that slot instances are not expected to contain the storage for the slot they manage β that will be in or attached to the class or object that they manage. those instances should provide their own implementations of theGetandSetmethods (and optionallyResolve; that defaults to a do-nothing pass-through). πΈ
most slot operations will initially Resolve their argument list, combine it with the results of the Get method, and then Set the result. some operations omit one or both of the first two steps; omitting the third would result in an idempotent read-only operation (but the standard mechanism for reading from slots is via info class and info object β that's the vibe π).
π EXAMPLES π
Okay bestie, this is the part where it all clicks. Both forms of oo::define and oo::objdefine work exactly the same way, and here's a lil demo showing that off plus four of their subcommands. Watch. π
oo::class create c
c create o
oo::define c method foo {} {
puts "world"
}
oo::objdefine o {
method bar {} {
my Foo "hello "
my foo
}
forward Foo ::puts -nonewline
unexport foo
}
o bar β prints "hello world"
o foo β error "unknown method foo"
o Foo Bar β error "unknown method Foo"
oo::objdefine o renamemethod bar lollipop
o lollipop β prints "hello world"
See how bar snuck in and called my Foo (which forwards to ::puts -nonewline) and then my foo? And then foo got unexported so it's β¨ gone β¨ from the outside, and bar got renamed to lollipop β and it still works. Periodt.
Next up: mixing extra classes into an object, and showing you that mixin is a slot that supports appending. π
oo::object create inst
inst m1 β error "unknown method m1"
inst m2 β error "unknown method m2"
oo::class create A {
method m1 {} {
puts "red brick"
}
}
oo::objdefine inst {
mixin A
}
inst m1 β prints "red brick"
inst m2 β error "unknown method m2"
oo::class create B {
method m2 {} {
puts "blue brick"
}
}
oo::objdefine inst {
mixin -append B
}
inst m1 β prints "red brick"
inst m2 β prints "blue brick"
Levitating. π§ inst started out knowing literally nothing, then mixin A gave it m1, then mixin -append B stacked m2 on top without yeeting A. Both bricks. Both slay.
Now class variables, bestie β this one's a class that counts how many instances of itself exist. It's giving census. π
oo::class create Counted
oo::define Counted {
initialise {
variable count 0
}
variable number
constructor {} {
classvariable count
set number [incr count]
}
method report {} {
classvariable count
puts "This is instance $number of $count"
}
}
set a [Counted new]
set b [Counted new]
$a report
β This is instance 1 of 2
set c [Counted new]
$b report
β This is instance 2 of 3
$c report
β This is instance 3 of 3
The initialise slot sets up count once at class-creation time, and every constructor reaches up into it with classvariable count. Iconic behaviour. π
Class methods! π (And yeah β heads up β the constructor for oo::class calls oo::define on the class. That's the tea.)
oo::class create DBTable {
classmethod find {description} {
puts "DB: locate row from [self] matching $description"
return [my new]
}
classmethod insert {description} {
puts "DB: create row in [self] matching $description"
return [my new]
}
method update {description} {
puts "DB: update row [self] with $description"
}
method delete {} {
puts "DB: delete row [self]"
my destroy; # Just delete the object, not the DB row
}
}
oo::class create Users {
superclass DBTable
}
oo::class create Groups {
superclass DBTable
}
set u1 [Users insert "username=abc"]
β DB: create row from ::Users matching username=abc
set u2 [Users insert "username=def"]
β DB: create row from ::Users matching username=def
$u2 update "group=NULL"
β DB: update row ::oo::Obj124 with group=NULL
$u1 delete
β DB: delete row ::oo::Obj123
set g [Group find "groupname=webadmins"]
β DB: locate row ::Group with groupname=webadmins
$g update "emailaddress=admins"
β DB: update row ::oo::Obj125 with emailaddress=admins
Look at that β [self] inside a classmethod gives you the class itself, so Users insert prints ::Users. And check out the method delete one: it calls my destroy to delete the object, not the DB row, and the comment says exactly that. Don't get it twisted. π₯
Last one and it's a big one, bestie: making a custom definition for a class. This explicitly includes delegation to the existing definition commands via namespace path. So smart, so β¨.
namespace eval myDefinitions {
# Delegate to existing definitions where not overridden
namespace path ::oo::define
# A custom type of method
proc exprmethod {name arguments body} {
tailcall method $name $arguments [list expr $body]
}
# A custom way of building a constructor
proc parameters args {
uplevel 1 [list variable {*}$args]
set body [join [lmap a $args {
string map [list VAR $a] {
set [my varname VAR] [expr {double($VAR)}]
}
}] ";"]
tailcall constructor $args $body
}
}
# Bind the namespace into a (very simple) metaclass for use
oo::class create exprclass {
superclass oo::class
definitionnamespace myDefinitions
}
# Use the custom definitions
exprclass create quadratic {
parameters a b c
exprmethod evaluate {x} {
($a * $x**2) + ($b * $x) + $c
}
}
# Showing the resulting class and object in action
quadratic create quad 1 2 3
for {set x 0} {$x <= 4} {incr x} {
puts [format "quad(%d) = %.2f" $x [quad evaluate $x]]
}
β quad(0) = 3.00
β quad(1) = 6.00
β quad(2) = 11.00
β quad(3) = 18.00
β quad(4) = 27.00
The exprmethod wraps a body in expr, and parameters builds a constructor that doubles each incoming argument and stashes them as variables. Then quadratic create quad 1 2 3 runs that constructor, and the whole thing evaluates a quadratic. Math class but make it fashion. π¦
π SEE ALSO π
next(3tcl),oo::class(3tcl),oo::object(3tcl)
π KEYWORDS π
class,definition,method,object,slot
That's the whole page, bestie!! π«Ά This was TclOO, version 0.3, dated define(3tcl) β go forth and define some iconic classes. No cap, you're gonna do great. πβ¨π
π¦ where this page comes from
- ubuntu stonking: tcl9.0-doc 9.0.4+dfsg-1
- Copyright (c) 2007-2018 Donal K. Fellows
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms