I've been trying to use heavily extensibility capabilities of Tcl to
solve my problem. I'm doing some classes with TclOO (using Tcl 8.5.9 +
TclOO 0.6 or Tcl 8.6.b1 -- will soon try Tcl CVS as well). My classes
will have "getters" and "setters" methods (in the best Java style,
argh :-/), but instead of writing duplicated code, I did just:
oo::class create MyClass {
superclass Parent
property foo
property bar
}
property is a proc that is declared in oo::define namespace and
creates methods based on the arguments it receives. It declares
"setters" as [method set-$var {v} {my variable $var; set $var $v}]
plus some validation.
Somewhere, I need to know what variables are declared. [info class
variables] just tell me variables declared with the [variable]
keyword. [info object variables] don't seem to work well. Any tips
on that?
Another question is about class methods declared with [self
methods ...]. Is it possible to know what variables a class will use
from a class method, without any object?
An alternative to solve this problem would be tracking variables in a
list, but it doesn't seem elegant or right.
One more question is about inheritance. I can't get class methods to
be inherited. Look:
oo::class create Base {
self method foo {} {}
}
oo::class create Other {
superclass Base
}
# Now testing
Other foo
The test doesn't work. I tried to modify Other:
oo::class create Other {
self method foo {} {next}
}
No way either :-/
How inheritance of "static methods" occur? What about variables? How
child classes can use variables declared in superclasses? TclOO stuff
in the wiki is not very rich yet... What are the man pages where I can
read about that?
Thank you very much.
I'd do this:
upvar 0 [my varname $varName] var; set var $v
But frankly, [namespace upvar] is just as good:
namespace upvar [namespace current] $varName var; set var $v
> Somewhere, I need to know what variables are declared. [info class
> variables] just tell me variables declared with the [variable]
> keyword. [info object variables] don't seem to work well. Any tips
> on that?
Both of those introspectors deal with variables that are declared to
be automatically imported into a method. You've figured that out
already for [info class variables], but the [info object] version does
the same thing *for object-level declarations* (declared via the [self
method] declaration or within [oo::objdefine]).
You're possibly after [info object vars], but you probably want to
attach the description of what properties there are to the class
definition rather than trying to grok them at runtime. Are you making
the properties work via a configure method or are you having them as
direct methods, one per property?
> Another question is about class methods declared with [self
> methods ...]. Is it possible to know what variables a class will use
> from a class method, without any object?
>
> An alternative to solve this problem would be tracking variables in a
> list, but it doesn't seem elegant or right.
I don't completely understand what you're asking here - probably
because I'm not at my smartest right now - so can't answer properly.
In particular, I don't understand the problem with keeping a list in
the class-object (if you're not making a method per property).
> One more question is about inheritance. I can't get class methods to
> be inherited. Look:
>
> oo::class create Base {
> self method foo {} {}
> }
> oo::class create Other {
> superclass Base
> }
> Other foo
>
> The test doesn't work.
[...]
Because they're not inheritable. (In languages where they are
inheritable, it's generally considered bad practice to make use of the
feature.)
> How inheritance of "static methods" occur?
It doesn't. They're ordinary methods on the class objects themselves,
plus magic. (It's possible to add extra magic to make the kind of
inheritance you're asking for work, but it's much more intrusive.)
> What about variables? How child classes can use variables declared
> in superclasses?
All variables are just variables in the object's namespace; a variable
'x' in a method in class Foo is the same as 'x' in a method in class
Bar that derives from it, since they're actually the same variable
belonging to the instance. However, class-level variables are
variables in the class object's namespace; [upvar] trickery can be
used to share them with the instances.
Does that answer what you were asking?
> TclOO stuff
> in the wiki is not very rich yet... What are the man pages where I can
> read about that?
There's a lot more clarity needed, but I know I don't know what to
write. I'm too close to it to write good tutorials, as I don't
understand what needs to be answered (but I can field questions just
fine).
Donal.
On Jan 15, 5:21 pm, "Donal K. Fellows"
<donal.k.fell...@manchester.ac.uk> wrote:
> On Jan 15, 12:29 am, Silas <sila...@gmail.com> wrote:
> > An alternative to solve this problem would be tracking variables in a
> > list, but it doesn't seem elegant or right.
>
> I don't completely understand what you're asking here - probably
> because I'm not at my smartest right now - so can't answer properly.
> In particular, I don't understand the problem with keeping a list in
> the class-object (if you're not making a method per property).
It is probably a wrong way to think about that, maybe you can help me.
I'm using Tcl extensibility capacities to auto-build classes that I
can use somewhere else. So, as you probably got it, instead of
creating methods, etc., I defined a "property" proc in the oo::define
namespace so it creates methods for me. Creating the methods, though,
is not enough. I need to store a list of the properties to use it
later.
I can, from the [property] proc, upvar a definition like [method foo
{args} {body}] but I can't upvar something like [lappend varlist
$myvar], because, even if [lappend] works in a class declaration scope
(does it?), it is probably not referencing varlist as a class (or
object) variable.
I don't know if this explanation is good enough... Any clue about
that?
Thanks in advance.
If it's being kept in the class, then you can do this (leaving out
lots of stuff and assuming that you're just appending the raw variable
name):
proc oo::define::property {name} {
# What class are we defining?
set cls [lindex [info level -1] 1]; # Magic alert!
...
# Make the class's 'varlist' variable available
namespace upvar [info object namespace $cls] varlist vars
lappend vars $name
...
}
Donal.
I would make the argument (and I have already directly with Donal)
that the current inheritance model is broken for class (static)
methods. As mentioned at http://blog.jayfields.com/2007/04/ruby-class-methods.html,
Ruby fully supports class methods in a very clean manner, and Ruby on
Rails makes extensive use of this in it's implementation of the Active
Record pattern (http://en.wikipedia.org/wiki/Active_record_pattern).
In the Active Record pattern, the table itself is represented as a
class, and each returned row is an instance of that class. You start
by doing operations on the class to create or retrieve rows of the
table and then use access methods on the instance to manipulate the
columns. For example (from my implementation of ActiveRecord::Base in
TclOO:
(test) 50 % set device [Device last] ;# Device inherits from
superclass ActiveRecord::Base
::oo::Obj8 ;# SQL generated:
"SELECT * FROM `devices` ORDER BY `devices`.`id` DESC LIMIT 1"
(test) 51 % $device id ;# row columns are automatically
created as instance methods
373
(test) 52 % $device name
te3930-03
(test) 53 % $device created_at
2011-01-18 16:05:59
(test) 54 % $device name "new_name" ;# If supplied with additional
parameter, the method *sets* the new value
new_name
(test) 55 % $device name
new_name
(test) 56 % $device updated_at ;# Supports some
automatically set columns
2011-01-18 16:05:59
(test) 57 % $device save ;# generates: "UPDATE
`devices` SET `name` = 'new_name', `updated_at` = '2011-01-21
15:14:52' WHERE `id` = 373"
true
(test) 58 % $device updated_at
2011-01-21 15:14:52
The Device class in this example is inherited from the
ActiveRecord::Base superclass that contains the methods to create or
retrieve row instances.
In my TCL implementation of Ruby on Rails ActiveRecord::Base (http://
api.rubyonrails.org/classes/ActiveRecord/Base.html) I was forced to do
a trick in order to get class methods to work for me. By using
"method unknown" and knowledge of the name of the base class you *can*
sort of force class methods to work as shown above with "Device
last". Using your example:
> oo::class create Base {
> self method foo {} {}
> }
> oo::class create Other {
> superclass Base
self method unknown args { Base {*}$args }
> }
> Other foo
Now if you call "Other foo" the method "foo" isn't found in class
Other so "Base foo" is executed.
The {*} is used to explode the $args list into separate arguments to
foo, the *first* of which is the name of the method to call in class
Base.
This is not as clean as with Ruby, but once I got it working I just
moved on rather than fight that battle.
I would make the argument that since TclOO most resembles Ruby in it's
dynamic extensibility (rather than the more rigid C++ or Java) that
TclOO should support class methods. In any case, the way that the
inherited classes inherit the *instance* methods makes no sense to me
at all, since the inherited class is NOT an instance of the parent
class and may have none of the same variables and such.
Hope that trick works for you,
David Schmidt
And I'd argue that those are two unrelated things. Being dynamic
doesn't make TclOO like Ruby; it makes it like Tcl. :-) Given that,
the issue is not that Tcl(OO) can't have methods on its classes, but
rather that the methods don't work like you've come to expect through
exposure to some other system (Ruby).
Donal.