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

Reply via email to