define(n)

πŸ’– define(n) β€” TclOO Commands πŸ’–

okay bestie, welcome to define(n), straight out of the TclOO Commands manual. this one's a whole saga so buckle up πŸŽ€


✨ NAME ✨

oo::define, oo::objdefine, oo::Slot β€” the trio that defines and configures classes and objects. no cap, this is where the main character energy lives πŸ‘‘


🌸 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 here's the tea. oo::define is your girl for controlling the configuration of classes, and oo::objdefine does the exact same thing but for objects (and yes, classes count as instance objects too, we love a crossover ✨). whatever config you're applying gets applied to the entity named in the class or the object argument.

and listen, the ripple effect is real: configuring a class also updates the configuration of every subclass of that class, and every object that's an instance of that class or that mixes it in (as modified by any per-instance configuration). it's giving generational impact πŸ’«

the way the configuration happens is controlled by either:

  • the defScript argument, or
  • the subcommand plus the following arg arguments.

when the second one's in play, it's exactly as if all the arguments from subcommand onwards got made into a list and that list was used as the defScript argument. same thing, different outfit πŸ’β™€οΈ

πŸ“Œ quick note bestie: the constructor for oo::class will call oo::define on the script argument that it's provided. that's the convenient little shortcut for creating and defining a class in one single step. iconic behavior.

πŸ‘‘ CONFIGURING CLASSES πŸ‘‘

these are the commands you can use inside the defScript for oo::define, and every single one of them can also be used in the subcommand form:

  • classmethod name ?argList bodyScript? β€” this creates a class method, or (if argList and bodyScript are omitted) promotes an existing method on the class object to be a class method. the name, argList and bodyScript arguments work just like in the method definition below. πŸ‘‡

class methods can be called on either the class itself or on the instances of that class. when they get called, the current object (peep the sel and my commands) is:

  • the class on which they're called, or
  • the class of the instance on which they're called,

depending on whether they were called on the class or on an instance of the class, respectively. and if they're called on a subclass or an instance of the subclass? the current object is the subclass. yes queen, inherit the throne πŸ‘‘

and 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. okay?? specific. we respect it.

  • constructor argList bodyScript β€” creates or updates the constructor for a class. the formal arguments to the constructor (defined using the same format as for the Tcl proc command) will be argList, and the body of the constructor will be bodyScript. when the body of the constructor gets evaluated, the current namespace of the constructor will be a namespace that is unique to the object being constructed 🌱. inside the constructor, you should use the next command to call the superclasses' constructors. and if bodyScript is the empty string, the constructor gets deleted. poof πŸ’¨

classes do not need to have a constructor defined, bestie. if none is specified, the superclass's constructor gets used instead. lazy girl summer but make it inheritance.

  • destructor bodyScript β€” creates or updates the destructor for a class. destructors take no arguments, and the body of the destructor is bodyScript. the destructor is called when objects of the class are deleted, and when called it will have the object's unique namespace as the current namespace. destructors should use the next command to call the superclasses' destructors.
⚠️ heads up: destructors are not called in all situations (e.g. if the interpreter is destroyed). also if bodyScript is the empty string, the destructor gets deleted.
⚠️ and another one: errors during the evaluation of a destructor are not returned to the code that causes the destruction of an object. instead, they get passed to the currently-defined bgerror handler. so like, don't expect a refund on your error, it goes to a different department 😭
  • 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 might actually be defined by a superclass; subclass exports override superclass visibility, and may in turn be overridden by instances. it's a whole hierarchy of vibes πŸ’…
  • forward name cmdName ?arg ...? β€” creates or updates a forwarded 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. the cmdName will always be resolved using the rules of the invoking objects' namespaces β€” i.e., when cmdName isn't fully-qualified, the command gets 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 if name starts with a lower-case letter, and non-exported otherwise.

and if you're in a private definition context (see the private definition command, below πŸ‘‡), this command creates private forwarded methods.

  • initialise script
  • initialize script β€” this evaluates script in a context which supports local variables and where the current namespace is the instance namespace of the class object itself. super useful for setting up, e.g., class-scoped variables. glow up your class setup ✨
  • method name ?option? argList bodyScript β€” creates or updates a method implemented as a procedure-like script. the name of the method is name, the formal arguments to the method (defined using the same format as for the Tcl proc command) 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's unique to the current object 🫢.

the method will be exported if name starts with a lower-case letter, and non-exported otherwise; this behavior can be overridden via export and unexport, or by specifying -export, -private or -unexport in the optional parameter option.

and if you're 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 the script (or the list of command and arguments given by cmd and args) in a context where the definitions made on the current class will be private definitions πŸ™ˆ.

these class definition commands are the ones 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. and all the 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 calling oo::objdefine on the class being defined (see CONFIGURING OBJECTS below for a description of the supported values of subcommand). it follows the same general pattern of argument handling as the oo::define and oo::objdefine commands, 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. and if you're 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) lets you alter the superclasses of the class being defined. each className argument 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. also, an empty parent class is equivalent to oo::object, and the parent classes of oo::object and oo::class may 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 the my command visible in each object's context) by the class being defined. note that the methods themselves might actually be defined by a superclass; subclass unexports override superclass visibility, and may be overridden by instance unexports. we're taking the spotlight away, respectfully πŸ’…
  • 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 actually be 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.

okay so real talk bestie, this next slot is a whole ✨vibe✨ on its own 🫢

If you're in a private definition context (peep the private definition command, like, below πŸ‘‡), this slot is the one messing with the list of private variable bindings for this class. And in a private variable binding, the name of the variable inside the instance object is different from the name you gave it in the definition 😌. The name you used in the definition is the one you use to reach that variable inside the methods of this class, and the name of the variable in the instance namespace gets a unique prefix so that accidentally using it from other classes is, like, extremely unlikely πŸ’…. no cap.


πŸ¦‹ ADVANCED CLASS CONFIGURATION OPTIONS πŸ¦‹

The following definitions are also supported, but they are not required in simple programs β€” so if you're just vibing with the basics, you can literally skip this whole section. But if you're built different, come thru πŸŽ€

  • definitionnamespace ?kind? namespaceName β€” okay so this one gives you control over what namespace the oo::define and oo::objdefine commands use to look up the definition commands they use πŸ‘‘. Whenever any object has a definition operation applied to it, the class it's an instance of (plus its superclasses and mixins) gets consulted for which definition namespace to use. oo::define grabs the class definition namespace, and ::oo::objdefine grabs the instance definition namespace, but other than that, both do the identical lookup operation, fr fr.

This sets the definition namespace of kind kind provided by the current class to namespaceName. Now here's the thing β€” namespaceName has to refer to a namespace that already exists right now, or it has to be the empty string (which stops the current class from having such a namespace connected, like a lil breakup πŸ’”). And kind, if you even supply it, has to be either -class (that's the default bestie) or -instance β€” that's how you say whether you're setting the namespace for use with oo::define or oo::objdefine respectively.

🚨 tea time: 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. The consequence of this is that actually using this feature for classes requires you to define a metaclass. it's giving locked-down, iykyk 😭
  • deletemethod name ?name ...? β€” deletes each of the methods called name from a class πŸ—‘οΈ. The methods must have previously existed in that class, so don't come for me if you try to delete something that was never there πŸ™ˆ. It does not affect the superclasses of the class, and it does not affect the subclasses or instances of the class (except when they have a call chain through the class being modified) or the class object itself. periodt.
  • filter ?-slotOperation? ?methodName ...? β€” this slot (peep SLOTTED DEFINITIONS below πŸ‘‡) sets or updates the list of method names that are used to guard whether a method call to instances of the class may even be called, and what the method's results are 🌸. Each methodName names a single filtering method (which may be exposed or not exposed) β€” and no, it is not an error for a non-existent method to be named, since subclasses might define them later. iconic behavior. By default, this slot works by appending.
  • mixin ?-slotOperation? ?className ...? β€” this slot (see SLOTTED DEFINITIONS below) sets or updates the list of additional classes that are to be mixed into all the instances of the class being defined 🧚. Each className argument names a single class that's getting mixed in. By default, this slot works by replacement β€” so watch out, bestie, it's not additive by default.
  • renamemethod fromName toName β€” renames the method called fromName in a class to toName ✨. The method must have previously existed in the class, and toName must not previously refer to a method in that class (so no name collisions, we don't do that here πŸ’…). It does not affect the superclasses of the class, and it does not affect the subclasses or instances of the class (except when they have a call chain through the class being modified), or the class object itself. And it does not change the export status of the method β€” if it was exported before, it will be afterwards. no take-backs, no plot twists 😌

and bestie we are NOT done yet, there's more coming, stay with me 🫢

✨ CONFIGURING OBJECTS ✨

okay bestie so these are the commands you can use in the defScript for oo::objdefine πŸ’– and literally every single one of them can also be used in the subcommand form, no cap. Let's go 🫢


  • export name ?name ...? β€” this one 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 tea: the methods themselves may actually be defined by a class or superclass β€” object exports override class visibility. πŸ‘‘
  • forward name cmdName ?arg ...? β€” creates or updates a forwarded object method called name. The method is defined 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 the method subcommand. 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 β€” 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 Tcl proc command) 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, -private or -unexport in the optional parameter option, or via the export and unexport definitions.

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 the my command 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 🌈

The following definitions are also supported, but are not required in simple programs β€” these are for when you're feeling extra, main character energy only 😌✨

  • 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.
🚨 bestie listen. the constructors do NOT run. if you just swap the class and walk away, your object can be a whole mess. do the extra config work, i'm begging 😭
  • 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 through oo::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 a oo::objdefine forwarded 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 πŸ”’

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 is as if the private methods do not exist. πŸ™ˆ 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. Private methods precede all mixed in classes in the method call order (as reported by self call).

✨ iykyk β€” that last bit is the whole point. private methods are how a class gossips with its own instances without the rest of the hierarchy hearing a word. periodt. πŸ’œ

πŸ’– SLOTTED DEFINITIONS πŸ’–

Okay bestie, this is where it gets fun. Some of the configurable definitions of a class or object are what we call slotted definitions. What that means is: the configuration is implemented by a slot object, which is literally an instance of the class oo::Slot, and that little icon manages a list of values (class names, variable names, whatever) that makes up the contents of the slot. Slay. ✨

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. no dupes allowed, periodt.
  • slot -clear β€” sets the slot definition to the empty list. it's giving clean slate 🧹
  • slot -prepend ?member ...? β€” prepends the given member elements to the slot definition. front of the line energy πŸ‘‘
  • slot -remove ?member ...? β€” removes the given member elements from the slot definition. bye bestie πŸ’…
  • slot -set ?member ...? β€” replaces the slot definition with the given member elements. full glow-up ✨

A consequence of this is that any use of a slot's default operation where the first member argument begins with a hyphen will be an error. One of the above operations should be used explicitly in those circumstances.

fr fr, don't just raw-dog a -something as your first member. use the actual operation. πŸ™ˆ

You only need to make an instance of oo::Slot if you are defining your own slot that behaves like a standard slot. iykyk.


πŸ¦‹ SLOT IMPLEMENTATION πŸ¦‹

Internally, slot objects also define a method --default-operation which is forwarded to the default operation of the slot (so, for the class β€œvariable” slot, this is forwarded to β€œmy -append”), plus 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 to oo::define or oo::objdefine. This method 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 to oo::define or oo::objdefine. This method 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 (as 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. πŸ’€

  • slot Resolve element β€” This converts an element of the slotted collection into its resolved form; for a simple value, it could just return the value, but for a slot that contains references to commands or classes it should convert those into their fully-qualified forms (so they can be compared with string equals): that could be done by forwarding to namespace which or similar.
  • 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 to oo::define or oo::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 this is necessary in order 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.) 🫢

The implementation of these methods is slot-dependent (and responsible for accessing the correct part of the class or object definition). 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.

heads up, bestie: mess with the slot mechanism itself and you're on your own. new ops should start with a hyphen. that's the vibe check. ⭐

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 the Get and Set methods (and optionally Resolve; 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).

so like… resolve ➑️ get ➑️ set is the whole pipeline. skip the set and you've got yourself a read-only vibe. 🌈

πŸ’– EXAMPLES πŸ’–

okay bestie, this is the fun part β€” four-ish little vignettes showing oo::define and oo::objdefine in the wild. This first one uses both forms of the commands (they work in the same way, fr fr) and flexes four of their subcommands. 🫢

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"

Next up: mixing extra classes into an object ✨ and yes, mixin is a slot that supports appending, so you can just keep stacking them.

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"

This one is so cute β€” creating and using class variables πŸŽ€. It's a class that counts how many instances of itself have been made. It's giving census energy.

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

Class methods time πŸ‘‘ β€” and a lil note for you: the constructor for oo::class calls oo::define on the class. iykyk.

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

And finally the main character move 🌈: making a custom definition for a class. Note that it explicitly includes delegation to the existing definition commands via namespace path.

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

🌸 SEE ALSO 🌸

  • next(n)
  • oo::class(n)
  • oo::object(n)

✨ KEYWORDS ✨

  • class
  • definition
  • method
  • object
  • slot

okay that's the whole page bestie, you made it to the end πŸ’œ go forth and define things, no cap. this lil legend comes from TclOO version 0.3, the define(n) page β€” slay, periodt πŸ¦‹πŸ’…

πŸ“¦ where this page comes from