diff options
author | Steve Bennett <steveb@workware.net.au> | 2010-10-22 20:32:31 +1000 |
---|---|---|
committer | Steve Bennett <steveb@workware.net.au> | 2010-12-16 08:10:39 +1000 |
commit | d69cd759e16a4572202f8e95e422604fb5725707 (patch) | |
tree | c592b212af9f76a9274b2b06965fbdb8e4690bda /README.oo | |
parent | 4f988c521cf54e2353ed4933fefcca4cb778bcdb (diff) | |
download | jimtcl-d69cd759e16a4572202f8e95e422604fb5725707.zip jimtcl-d69cd759e16a4572202f8e95e422604fb5725707.tar.gz jimtcl-d69cd759e16a4572202f8e95e422604fb5725707.tar.bz2 |
Add a pure-TCl OO implementation to Jim
And document the OO extension in README.oo
Signed-off-by: Steve Bennett <steveb@workware.net.au>
Diffstat (limited to 'README.oo')
-rw-r--r-- | README.oo | 253 |
1 files changed, 253 insertions, 0 deletions
diff --git a/README.oo b/README.oo new file mode 100644 index 0000000..ab8015c --- /dev/null +++ b/README.oo @@ -0,0 +1,253 @@ +OO Package for Jim Tcl +====================== + +Author: Steve Bennett <steveb@workware.net.au> +Date: 1 Nov 2010 09:18:40 + +OVERVIEW +-------- +The pure-Tcl oo package leverages Jim's unique strengths +to provide support for Object Oriented programming. + +The oo package can be statically linked with Jim or installed +as a separate Tcl package and loaded with: + + package require oo + +DECLARING CLASSES +----------------- +A class is declared with the 'class' proc as follows. + + class myclass ?baseclasses? classvars + +This declares a class named 'myclass' with the given dictionary, +'classvars', providing the initial state of all new objects. +It is important to list all class variables in 'classvars', even +if initialised only to the empty string, since the class makes +these variables available in methods and via [myclass vars]. + +A list of zero or more base classes may also be specified from +which methods and class variables are imported. See INHERITANCE +below for more details. + +Declaring a class creates a procedure with the class name along +with some related procedures. For example: + + . class Account {balance 0} + Account + . info procs Account* + {Account get} {Account methods} {Account eval} Account {Account new} {Account destroy} + {Account vars} {Account classname} {Account classvars} {Account method} + +Notice that apart from the main 'Account' procedure, all the remaining procedures (methods) +are prefixed with 'Account' and a space. + +PREDEFINED CLASS METHODS +------------------------ +Decaring a class pre-defines a number of "class" methods. i.e. those which don't +require an object and simply return or manipulate properties of the class. These are: + + new ?instancevars?:: + Creates and returns new object, optionally overriding the default class variable values. + Note that the class name is an alias for 'classname new {}' and can be used as a shorthand + for creating new objects with default values. + + method name arglist body:: + Creates or redefines a method for the class with the given name, argument list and body. + + methods:: + Returns a list of the methods supported by this class, including both class methods + and instance methods. Also includes base class methods. + + vars:: + Returns a list of the class variables for this class (names + only). Also includes base class variables. + + classvars:: + Returns a dictionary the class variables, including initial values, for this class. + Also includes base class variables. + + classname:: + Returns the classname. This can be useful as [$self classname]. + +Class methods may be invoked either via the class name or via an object of the class. +For example: + + . class Account {balance 0} + Account + . Account methods + classname classvars destroy eval get method methods new vars + . set a [Account] + <reference.<Account>.00000000000000000001> + . $a methods + classname classvars destroy eval get method methods new vars + +PREDEFINED OBJECT METHODS +------------------------- +Decaring a class pre-defines a number of "object" methods. i.e. those which operate +on a specific object. + + destroy:: + Destroys the object. This method may be overridden, but note that it should + delete the object with {rename $self ""}. This method will also be called + if the object is reaped during garbage collection. + + get varname:: + Returns the value of the given instance variable. + + eval ?locals? body:: + Makes any given local variables available to the body, along with + the instance variables, and evaluate the body in that context. + This can be used for one-off evaluation to avoid declaring a method. + +CREATING OBJECTS +---------------- +An object is created with the 'new' method, or simply by using the classname shortcut. +If the 'new' method is used, the variables for the newly created object (instance variables) +may be initialised. Otherwise they are set to the default values specified when the +class was declared. + +For example: + + . class Account {balance 0} + Account + . set a [Account] + <reference.<Account>.00000000000000000001> + . set b [Account new {balance 1000}] + <reference.<Account>.00000000000000000002> + . $a get balance + 0 + . $b get balance + 1000 + +DECLARING METHODS +----------------- +In addition to the predefined methods, new methods may be decared, or existing +methods redefined with the class method, method. + +Declaring a method is very similar to defining a proc, and the arglist +has identical syntax. For example: + + . Account method show {{channel stdout}} { $channel puts "Balance of account is $balance" } + . $b show + Balance of account is 1000 + +All instance variables are available within the method and any +changes to these variables are maintained by the object. + +In addition, the $self variables is defined and refers to the current object. +This may be used to invoke further methods on the object. For example: + + . Account method show {} { puts "Balance of account is [$self get balance]" } + . $b show + Balance of account is 1000 + +Notes: +* It is a bad idea to unset an instance variable. +* In general, you should avoid redefining any of the pre-defined methods, except for 'destroy'. +* When accessing the caller's scope with upvar or uplevel, note that there + are two frame levels between the caller and the method. Thus it is necessary + to use 'upvar 2' or 'uplevel 2' + +INHERITANCE +----------- +For each base class given in a new class declaration, the methods +and variables of those classes are imported into the new class being +defined. Base classes are imported in left to right order, so that if a +method is defined in more than one base class, the later definition +is selected. This applies similarly to class variables. + +Within a method, 'super' may be used to explicitly invoke a +base class method on the object. This applies only to the *last* +base class given. For example: + + # Assumes the existence of classes Account and Client + . Account method debit {amount} { incr balance -$amount } + . class CreditAccount {Client Account} {type visa} + CreditAccount + . CreditAccount method debit {amount} { + puts "Debit $type card" + super debit $amount + } + . set a [CreditAccount] + <reference.<Account>.00000000000000000001> + . $a debit 20 + Debit visa card + . $a balance + -20 + +In the CreditAccount debit method, the call to 'super debit' invokes +the method 'Account debit' since Account is the last base class listed. + +OBJECT LIFETIME/GARBAGE COLLECTION +---------------------------------- +Objects are implemented as lambdas. That is, they are procedures with state +and are named as references. This means that when an object is no longer +reachable by any name and garbage collection runs, the object will be +discarded and the destructor will be invoked. Note that the garbage collector +can be invoked manually with 'collect' if required. + + . class Account {} + Account + . Account method destroy {} { puts dying...; rename $self "" } + Account destroy + . proc a {} { set b [Account]; return "" } + a + . a + . collect + dying... + 1 + +CLASS METHODS/CLASS STATIC VARIABLES +------------------------------------ +All methods defined with 'method' operate on objects (instances). +If a class method is required, it is possible to simply declare one with 'proc'. +The method dispatcher will automatically be able to dispatch to this method. +Using this approach, it is also possible to add class static variables by +defining static variables to the proc. Although strictly these variables +are accessible only to that proc, not the class as a whole. + +For example: + + . class Account {} + Account + . proc {Account nextid} {} {{id 0}} { incr id } + Account nextid + . Account nextid + 1 + . Account nextid + 2 + . set a [Account] + <reference.<Account>.00000000000000000001> + . $a nextid + 3 + . $a eval { $self nextid } + 4 + +HOW METHOD DISPATCH WORKS +------------------------- +All class and object methods are name "classname methodname". + +The class method dispatcher is named "classname". When invoked with a methodname, +it simply invokes the method "classname methodname". + +The method dispatch is via a two step process. Firstly the object procedure is invoked +with the method name. This procedure then invokes "classname method" which sets up +the appropriate access to the object variables, and then invokes the method body. + +EXAMPLES +-------- +tree.tcl +~~~~~~~~ +The 'tree' package is implemented using the 'oo' package. +See the source code in tree.tcl and a usage example in tests/tree.test + +Of particular note is how callbacks and recursive invocation is used in the 'walk' method. + +examples/ootest.tcl +~~~~~~~~~~~~~~~~~~~ +A comprehensive OO example is provided in examples/ootest.tcl. + +It can be run simply as: + + ./jimsh examples/ootest.tcl |