In Aug 26, 2009, at 4:08 , Michael Carman wrote:
I finally got around to implementing the Tkx::Scrolled megawidget that
I'd been thinking about for a long while. In the process of doing so I
ran across some behavior regarding callbacks that I couldn't grok from
the documentation.
The basic case is straightforward enough:
$text->configure(-yscrollcommand => [$scrollbar, 'set']);
This calls C<$scrollbar->set(first, last)> (where "first" and "last"
are
the range of $text that's visible). I call this straightforward
because
it's simple and matches the behavior of Perl/Tk, but I haven't seen
this
syntax actually documented anywhere.
The reason this works is that Tkx::widget objects stringify as the
name of the widget they wrap and the fact that this name is actually a
command in Tcl's own namespace. The effect of the statement above is
the same as if you did:
$text->configure(-yscrollcommand => "$scrollbar set");
When the callback happens it's all handled on the Tcl side of things.
The next case is a little odd:
$text->configure(-yscrollcommand => [\&_set, 'foo']);
This calls C<_set(first, last, 'foo')>. It seems odd to me that 'foo'
appears at the end rather than at the beginning. It's particularly
awkward if you want to pass an object because "$self" won't end up
being
the first argument to the method. This append vs. prepend behavior
doesn't appear to be documented.
Right. It's behaviour inherited from Tcl.pm's call function, but it's
not really documented there either.
This form is treated the same as:
-yscrollcommand => [\&_set, Tkx:Ev(), 'foo']
Which brings up the question what order args appear in if you provide
stuff in Tkx::Ev() to be expanded. I think the order is:
1. arguments that the command/function is called with from the tcl
side
2. the expanded Tkx::Ev() args
3. the rest of the values from the perl array as passed
The documentation should be enhanced to explain this.
The weirdest case is this one:
$text->configure(-yscrollcommand => \&_set);
If I dump @_ in _set() I get this:
$VAR1 = [
undef,
bless( do{\(my $o = 58361464)}, 'Tcl' ),
'::perl::CODE(0x37c7cdc)',
'0',
'1'
];
Here the first/last values appear at the end. The second argument
appears to be the Tcl interpreter (from Tcl.pm). The third argument is
some sort of blessed reference to the subroutine being called,
although
the stringification is unusual. I have no idea why there's a leading
undef value, or what it's supposed to be a placeholder for. The
documentation only states that you can pass a CODE reference to a
callback. It says nothing about what the arguments are.
Right. The 3 first arguments are basically junk. Functions passed
this way should really start out with doing a splice(@_, 0, 3) :-(
This behaviour is also inherited from Tcl.pm's call() function. This
isn't really documented either but the description of the
Tcl::CreateCommand() give some hints about this.
The story is that Perl functions registered with CreateCommand are
always called back with (CLIENTDATA, INTERP, LIST) as arguments. The
CLIENTDATA returns a scalar that was passed to CreateCommand, but for
the implicit callbacks created by call() these are always undef. The
INTERP is the reference to the perl 'Tcl' object. The LIST is the
command with arguments exactly as it was invoked from the Tcl side.
From this you can see that the Tcl code in your dump above invoked the:
"::perl::CODE(0x37c7cdc)" 0 1
command. The funny name "::perl::CODE(0x37c7cdc)" is actually the
name this callback function has registered in the Tcl namespace. The
code reference was just stringified to generate a unique name and then
placed in the "::perl::" namespace.
My preference would be to change this interface so that the 3 first
args were always chopped. That would make passing [\&foo] and \&foo
have exactly the same behaviour. The best way to fix this would
actually be on the Tcl.pm side, but if this isn't acceptable Tkx could
always preprocess the arguments it pass to rewrite \&foo into
[\&foo]. I would not document this until we figured out how/if we
want to fix this.
Hope this clarifies things.
If you want to help out by enhancing the documentation consider
cloning <http://github.com/gisle/tkx/>. I'm also happy to give you
direct 'push' access to this repo if you want.
--Gisle