Dear XOTcl community,
we are working currently on various issues which we think are important for our 1.0 release:
- speedup through byte-code support - better components with introspection facilities - easy to use rel-db interface ...
concerning the first item: i was performing some experiments for providing extension-specific byte-code support, e.g. to allow to register byte-code instructions for the most-used commands in XOTcl (such as [self], next, etc.). through these xotcl-specific bytre-codes we get nice improvements (e.g. [self] is more than 10 times faster, some benchmarks improve by 40%). This improvements require a patch for tcl, which is therefore not acceptable for most people now. We talked to the Tcl implementors to include such a patch, but currently some of them think about a substantial redesign of the bytecode which has much more constraints once extension writers depend on the current design, so i don't expect that a patch with this functionality will be intergrated in tcl in the new future.
however, there is already some influence for XOTcl: In Tcl, compilation is command based: this means, for every call of a command literally available in the the body of a proc, a bytecode sequence can be provided by the command. This means that it is possible to compile the body of method m1 in Object o o proc m1 {} { o set x 123 } o proc m2 {} { [self] set x 234 } to something clever, but not the body of m2, since the command in the body of m2 depends on the results of [self] which the compiler does not know. Since m2 is the much better style, we should address this principal problem, since this will ease future improvements for xotcl. An easy soultion is to provide a tcl-command selfInvoke, which can be byte-compiled. selfInvoke has the following sematics
proc selfInvoke args { eval [self] $args }
and if implemented in C, it leads already to some speedup (without bytecode support), since the call of the tcl command "self" is replaced internally by a call to a c-function.
Another advantage is that the number of [self] in an xotcl script is reduced, an programs might become easier to type ()since less shilft/alt/.. keys have to be pressed) and hopefully less "cluttered" to read.
yet another advantage is that this change makes in a program more visible when a delegation is used (call to a different object) instead of the call of the current object.
since "selfInvoke" is not something i would like to type often either, and this is rather a language primitive, we are considering some syntactical alternatives, and want some input from the "community". Certainly, the explicit call of [self] or to another object will be supported in the future as well.
what do you think about the following variants:
o proc m1 {} { [self] set x 123 } o proc m2 {} { .. set x 123 } o proc m3 {} { = set x 123 } o proc m4 {} { => set x 123 } o proc m5 {} { - set x 123 }
other ideas?
note, that the used symbol cannot be used as an (global) object name (same as with next, self, set, and other global tcl commands)
what do you think?
best regards -gustaf neumann
On Wednesday 20 February 2002 16:28, you wrote:
we are working currently on various issues which we think are important for our 1.0 release:
Ok. The 1.0 is on the horizon :) This is good news!
- speedup through byte-code support
I'd say, post a core-patch as-is on the website, so people who'd like to use it can grab it from there. I'm certainly going to try it, as soon as I get some relief from the current pressure at work.
- better components with introspection facilities
I'm already perfectly satisfied, but I'm sure others would add something to this.
- easy to use rel-db interface
I'm not using relational databases, so I can't say much about it.
- ...
Here I could add a thing :)
I'd like to see some kind of automatic object garbage collection, for example, when an object gets defined within a scope of a proc, I'd like it to get auto-destroyed when this proc exits.
It could be something like:
Class foo foo instproc init {args} { puts stderr "[self] comming" next } foo instproc destroy {args} { puts stderr "[self] leaving" next } foo instproc test {args} { puts "Hallo World" } proc dummy {args} { foo private fooVar ; # "private" might be a new constructor method $fooVar test return }
When somebody calls "dummy" proc, a new "foo" object gets created (the "private" method" and automatically destroyed when proc returns. Can such thing be done with simple Tcl variable traces, hm?
what do you think about the following variants:
o proc m1 {} { [self] set x 123 } o proc m2 {} { .. set x 123 } o proc m3 {} { = set x 123 } o proc m4 {} { => set x 123 } o proc m5 {} { - set x 123 }
other ideas?
What about:
o proc m6 {} { this set x 123 }
The "this" command refers to the current object, like [self] does.
Cheer's Zoran
On Thursday 21 February 2002 18:17, Zoran Vasiljevic wrote:
Class foo foo instproc init {args} { puts stderr "[self] comming" next } foo instproc destroy {args} { puts stderr "[self] leaving" next } foo instproc test {args} { puts "Hallo World" } proc dummy {args} { foo private fooVar ; # "private" might be a new constructor method $fooVar test return }
When somebody calls "dummy" proc, a new "foo" object gets created (the "private" method" and automatically destroyed when proc returns. Can such thing be done with simple Tcl variable traces, hm?
Hm, what about this little baby:
proc ::xotcl::gc {name1 name2 op} { ::uplevel $name1 destroy }
Class instproc private {varname args} { ::upvar $varname v ::set v $varname ::uplevel trace variable $varname u ::xotcl::gc ::eval [self] create $varname $args }
Yields...
% Class foo % foo private bar % $bar wrong # args: should be {bar message ?args...?} % set bar ::bar % unset bar % ::bar invalid command name "::bar"
Cheer's Zoran
Hm, what about this little baby:
proc ::xotcl::gc {name1 name2 op} {
::uplevel $name1 destroy
}
Class instproc private {varname args} {
::upvar $varname v ::set v $varname ::uplevel trace variable $varname u ::xotcl::gc ::eval [self] create $varname $args
}
Hi Zoran, very nice!
...below is a still simpler version...
with the same idea, we we can make a bind command, where we can bind an object to a variable in the sense that
a) the object is deleted, when the variable is unset b) the object can provide a print-value, when someone read the value of the variable
this might be useful for e.g. associations as well, where you have an instance variable refering to an object, and once you delete the object (with its variables) you want to destroy the referred object (or decrement a reference counter)
-gustaf PS: i have a little bad feeling about filters/mixins and uplevel/upvar and friends.
#### private proc ::xotcl::gc {name1 name2 op} { puts "destroying $name1" $name1 destroy }
Class instproc private {varname args} { ::uplevel trace variable $varname u ::xotcl::gc ::uplevel set $varname [::eval [self] create $varname $args] }
##### bind proc ::xotcl::value {name1 name2 op} { uplevel set $name1 [list [$name1 value]] }
Object instproc bind {object varname} { ::uplevel trace variable $varname u ::xotcl::gc ::uplevel trace variable $varname r ::xotcl::value ::uplevel set $varname $object }
#### usage Class foo foo private bar
Class C -parameter i C instproc value {} { return "[self]'s value is [[self] incr i]" }
Object o o proc test1 {} { Object private o1 puts ... o1 set x 1 puts ... } o proc test2 {} { C o2 -i 11 puts ... [self] bind o2 o2 puts ...$o2... puts ...$o2... }
puts ===== o test1 puts ===== o test2 puts =====
On Friday 22 February 2002 21:37, you wrote:
...below is a still simpler version...
[cut]
Ehm, I'd say we'd have to take some more care about encapsulation....
Consider procedure A calling procedure B calling procedure C. Each of them try to create a "private" object "bar" from the "foo" class. What happens?
The object from A gets overwritten from the B and then B gets overwritten from C. That's not good. We have to assure that all created object names are unique, since they are visible from all calling frames.
I've posted the better solution to the mailing list but it somehow never reached the list, so I'll try it again. Below you'll find my last working version. It uses a Tcl array to store variable-objectname bindings so we know which object to destroy when a named variable gets unset. This is needed since variable traces are invoked AFTER the variable has already been deleted. Furthermore, traces are invoked in the calling procedure frame if we have a variable deletion because the procedure is about to exit. This is tricky stuff, but I think i have it under control now. See for yourself... Oh yes, the Tcl array with bindings is kept in the callers frame.
we we can make a bind command, where we can bind an object to a variable in the sense that
a) the object is deleted, when the variable is unset b) the object can provide a print-value, when someone read the value of the variable
this might be useful for e.g. associations as well, where you have an instance variable refering to an object, and once you delete the object (with its variables) you want to destroy the referred object (or decrement a reference counter)
Indeed; "bind" is a very good name for this. We can also serialize the object when somebody does the read on the bound variable. This way one can pass objects per-value across function calls and also over remote procedure calls. Speaking of rpc, I have developed Tcl-like rcp using the XOTcl, AOLserver and http as transport so I can test the idea...
PS: i have a little bad feeling about filters/mixins and uplevel/upvar and friends.
Huh, let me think deeply about that...
Now the code... I changed "private" to "volatile" to emphasise the fact.
#------- CUT HERE -------
# # Gets invoked from variable unset callbacks. # Class proc __gc {n1 n2 op} { set l [::expr {[::info level] > 0}] if {![::uplevel $l ::info exists __gc($n1)]} { ::incr l } ::uplevel $l $__gc($n1) destroy }
# # Used to instantiate "volatile" objects. # These get auto-destroyed when the procedure goes out of context. # Class instproc volatile {n args} { ::upvar $n v ::trace variable v u [list Class __gc] ::set v [[self] autoname -instance [self]] ::uplevel [::expr {[::info level]<2?1:2}] ::set __gc($n) $v ::eval [self] create $v $args }
# # Test routines # proc aa {} { _foo_ volatile obj $obj test bb } proc bb {} { _foo_ volatile obj $obj test cc } proc cc {} { _foo_ volatile obj $obj test unset obj dd } proc dd {} { _foo_ volatile obj $obj test } proc gctest {} { Class _foo_ _foo_ instproc test {} {puts stderr "proc [info level -1] object [self]"} set cmds [info comm _foo_*] aa if {[info comm _foo_*] == $cmds} { puts stderr ok } else { puts stderr fail } }
#------- CUT HERE -------
In file xotcl.c:
static int XOTclCNewMethod(ClientData cd, Tcl_Interp* in, int objc, Tcl_Obj *objv[]) { XOTclClass *cl = XOTclObjectToClass(cd); XOTclObject *obj = &cl->object; Tcl_Obj *cname, *autoname; char *name, *n; int result, offset=1; DEFINE_NEW_TCL_OBJECTS_ON_STACK(objc+1, ov);
/* ^^^^^^ */
Should read:
DEFINE_NEW_TCL_OBJECTS_ON_STACK(objc+2, ov);
... because later in file, ov[0] and ov[1] are reserved for internal use and other arguments are copied over which results in trashing memory.
Cheers Zoran
On Monday 04 March 2002 12:15, you wrote:
In file xotcl.c:
static int XOTclCNewMethod(ClientData cd, Tcl_Interp* in, int objc, Tcl_Obj *objv[]) { DEFINE_NEW_TCL_OBJECTS_ON_STACK(objc+1, ov);
...
/* ^^^^^^ */
Should read:
DEFINE_NEW_TCL_OBJECTS_ON_STACK(objc+2, ov);
zoran, thank you. this bug is confirmed and fixed. -gustaf