Thread: RE: [tcltk-perl] Drop listify [PATCH] (Page 2)
Brought to you by:
hobbs
From: Jeff H. <je...@Ac...> - 2004-04-15 20:07:52
|
> Is there a semantic difference between [list a b c] and {a b c} > in Tcl the language? If not, then I don't think they should be > different when passed to perl either. OH man, oh man ... this is sticky. Yes and no. At the C level, the first is a pure list (perl AV), while the second is just a string. You can stringify the list and listify the string, just as you can convert an SvPV to an AV and back. The trick is that in passing a Tcl_Obj to become and SV, I check the object types first for efficient conversion (trying to reduce round-tripping through strings, which I think you've noticed the positive effect of). I think that I've properly handled it with the recent changes to call and icall. If something is still "not right", please tell me. It's important to be careful, because the way that Tcl will convert a string to a list is indifferent to extra whitespace. The following all represent the same thing in when listified: set var {a b c} set var { a b c } set var {a b c} Jeff |
From: Gisle A. <gi...@Ac...> - 2004-04-15 21:10:13
|
"Jeff Hobbs" <je...@Ac...> writes: > > Is there a semantic difference between [list a b c] and {a b c} > > in Tcl the language? If not, then I don't think they should be > > different when passed to perl either. > > OH man, oh man ... this is sticky. Yes and no. At the C level, > the first is a pure list (perl AV), while the second is just a > string. You can stringify the list and listify the string, just > as you can convert an SvPV to an AV and back. I read this answer to mean that there is no semantic difference. What it is represented as internally should not matter. There is no two-way conversion between strings and arrays in perl. > The trick is that > in passing a Tcl_Obj to become and SV, I check the object types > first for efficient conversion (trying to reduce round-tripping > through strings, which I think you've noticed the positive > effect of). I understand all that, but I don't think this should dictate the semantics. > I think that I've properly handled it with the recent changes to > call and icall. If something is still "not right", please tell > me. To me it wrong if: $tcl->Eval("set var {a b c}"); returns something different to perl than $tcl->Eval("set var [list a b c]"); if these two expressions have exactly the same semantics in Tcl. I would suggest that we always return the string in scalar context and always list in list context. If the value is not a valid list, then we croak in list context. Alternatively we return return some object that just wraps the Tcl_Obj and can used as both a string and array reference. This can be combined with the previous alternative, so this only happens in scalar context. Alternatively we have a separate methods that indicate if a string, array reference or list is wanted as return value. $tcl->Eval_giving_string($code); $tcl->Eval_giving_arrayref($code); $tcl->Eval_giving_list($code); --Gisle |
From: Gisle A. <gi...@Ac...> - 2004-04-21 13:08:33
|
Gisle Aas <gi...@Ac...> writes: > To me it wrong if: > > $tcl->Eval("set var {a b c}"); > > returns something different to perl than > > $tcl->Eval("set var [list a b c]"); > > if these two expressions have exactly the same semantics in Tcl. > > I would suggest that we always return the string in scalar context and > always list in list context. If the value is not a valid list, then > we croak in list context. If you try the following program: #!/usr/bin/perl -w use Tcl::Tk qw(:perlTk); my $mw = MainWindow->new; my $label = $mw->Label(-text => "Hello")->pack; if (my $file = $mw->getOpenFile) { $label->configure(-text => "File [$file]"); $mw->after(5000, sub { $mw->destroy }); } MainLoop; and then hit "Cancel" to the dialog that pops up, then we fill in the field with something like "File [ARRAY(0x8141ad8)]". Apparently tk_getOpenFile returns a list instead of string, but this is not documented. Since there is no semantic difference between a list and a string this does not really matter to Tcl, but for perl with the current Tcl.pm behaviour it does. I think we should make $tcl->Eval and $tcl->call behave as I describe in the quoted text above. That would fix this problem and in general make things sane. Is there a way to make an automatic test case out of this? I guess I'm asking if there is a way to make the program above hit the "Cancel" button by itself. In our private branch I have used this workaround for now: --- Tcl-Tk/lib/Tcl/Tk.pm.~1~ Wed Apr 21 05:43:50 2004 +++ Tcl-Tk/lib/Tcl/Tk.pm Wed Apr 21 05:43:50 2004 @@ -839,19 +839,25 @@ my $self = shift; my %args = @_; $args{'-parent'} = $self->path unless defined $args{'-parent'}; - $wint{$$self}->call('tk_getOpenFile', %args); + my @files = $wint{$$self}->call('tk_getOpenFile', %args); + return $files[0] unless wantarray; + return @files; } sub getSaveFile { my $self = shift; my %args = @_; $args{'-parent'} = $self->path unless defined $args{'-parent'}; - $wint{$$self}->call('tk_getSaveFile', %args); + my @files = $wint{$$self}->call('tk_getSaveFile', %args); + return $files[0] unless wantarray; + return @files; } sub chooseDirectory { my $self = shift; my %args = @_; $args{'-parent'} = $self->path unless defined $args{'-parent'}; - $wint{$$self}->call('tk_chooseDirectory', %args); + my @files = $wint{$$self}->call('tk_chooseDirectory', %args); + return $files[0] unless wantarray; + return @files; } sub messageBox { my $self = shift; End of Patch. > Alternatively we return return some object that just wraps the Tcl_Obj > and can used as both a string and array reference. This can be > combined with the previous alternative, so this only happens in scalar > context. > > Alternatively we have a separate methods that indicate if a string, > array reference or list is wanted as return value. > > $tcl->Eval_giving_string($code); > $tcl->Eval_giving_arrayref($code); > $tcl->Eval_giving_list($code); |
From: Jeff H. <je...@ac...> - 2004-04-21 16:40:00
|
> Gisle Aas <gi...@Ac...> writes: > > > To me it wrong if: > > $tcl->Eval("set var {a b c}"); > > returns something different to perl than > > $tcl->Eval("set var [list a b c]"); > > if these two expressions have exactly the same semantics in Tcl. > > > > I would suggest that we always return the string in scalar context and > > always list in list context. If the value is not a valid list, then > > we croak in list context. The thing is that there is a difference, it's just very, very subtle. The problem is that the subtle distinctions are very important. > If you try the following program: > > #!/usr/bin/perl -w > > use Tcl::Tk qw(:perlTk); > > my $mw = MainWindow->new; > my $label = $mw->Label(-text => "Hello")->pack; > if (my $file = $mw->getOpenFile) { > $label->configure(-text => "File [$file]"); > $mw->after(5000, sub { $mw->destroy }); > } > > MainLoop; > > and then hit "Cancel" to the dialog that pops up, then we > fill in the field with something like "File > [ARRAY(0x8141ad8)]". Apparently tk_getOpenFile returns a > list instead of string, but this is not documented. Since This is just a bad example. The fact that it returns anything on unix is wrong wrong wrong (and possibly a bug to look into). On Windows, the expected "" is returned and thus you never enter into that loop. Looking into the Tk code ... I'm suspecting bug, as I see that the variable returned is specifically set to "" (empty string, not empty list) when cancel is hit. The important thing in getOpenFile is in making sure whatever changes you do correctly handle files with spaces and multiple files (in case you pass -multiple 1 to the dialog). Jeff |
From: Gisle A. <gi...@Ac...> - 2004-04-21 18:04:26
|
"Jeff Hobbs" <je...@Ac...> writes: > > Gisle Aas <gi...@Ac...> writes: > > > > > To me it wrong if: > > > $tcl->Eval("set var {a b c}"); > > > returns something different to perl than > > > $tcl->Eval("set var [list a b c]"); > > > if these two expressions have exactly the same semantics in Tcl. > > > > > > I would suggest that we always return the string in scalar context and > > > always list in list context. If the value is not a valid list, then > > > we croak in list context. > > The thing is that there is a difference, it's just very, very > subtle. The problem is that the subtle distinctions are very > important. So tell me what breaks if we do it like this. > > If you try the following program: > > > > #!/usr/bin/perl -w > > > > use Tcl::Tk qw(:perlTk); > > > > my $mw = MainWindow->new; > > my $label = $mw->Label(-text => "Hello")->pack; > > if (my $file = $mw->getOpenFile) { > > $label->configure(-text => "File [$file]"); > > $mw->after(5000, sub { $mw->destroy }); > > } > > > > MainLoop; > > > > and then hit "Cancel" to the dialog that pops up, then we > > fill in the field with something like "File > > [ARRAY(0x8141ad8)]". Apparently tk_getOpenFile returns a > > list instead of string, but this is not documented. Since > > This is just a bad example. The fact that it returns anything > on unix is wrong wrong wrong (and possibly a bug to look into). > On Windows, the expected "" is returned and thus you never > enter into that loop. > > Looking into the Tk code ... I'm suspecting bug, as I see that > the variable returned is specifically set to "" (empty string, > not empty list) when cancel is hit. So why was it returning a reference to an empty array then? Some bug in the Tcl.pm interface code? > The important thing in getOpenFile is in making sure whatever > changes you do correctly handle files with spaces and multiple > files (in case you pass -multiple 1 to the dialog). If you call $mw->getOpenFile in list context it should all work. And if you provide -multiple 1 you probably know that you must do that. --Gisle |
From: Jeff H. <je...@ac...> - 2004-04-21 18:14:40
|
> > The thing is that there is a difference, it's just very, very subtle. > > The problem is that the subtle distinctions are very important. > > So tell me what breaks if we do it like this. With your changes, it all remains correct, but you have to make those changes only to work around a bug IMO. You cannot generally make the statement that all Tcl lists should be scalar-ized, as that will ruin performance without purpose. > > Looking into the Tk code ... I'm suspecting bug, as I see that the > > variable returned is specifically set to "" (empty string, not empty > > list) when cancel is hit. > > So why was it returning a reference to an empty array then? > Some bug in the Tcl.pm interface code? Haven't delved into it yet ... > > The important thing in getOpenFile is in making sure whatever changes > > you do correctly handle files with spaces and multiple files (in case > > you pass -multiple 1 to the dialog). > > If you call $mw->getOpenFile in list context it should all > work. And if you provide -multiple 1 you probably know that > you must do that. Yes, that is correct. I just want to understand the bug that we are hitting to see that it's clearly a bug, and not a subtle difference that we have to consider. Jeff |
From: Gisle A. <gi...@Ac...> - 2004-04-21 18:31:37
|
"Jeff Hobbs" <je...@Ac...> writes: > > > The thing is that there is a difference, it's just very, very subtle. > > > The problem is that the subtle distinctions are very important. > > > > So tell me what breaks if we do it like this. > > With your changes, it all remains correct, but you have to > make those changes only to work around a bug IMO. You cannot > generally make the statement that all Tcl lists should be > scalar-ized, as that will ruin performance without purpose. I don't believe it will hurt performance, especially if you look for void context and avoids doing any work at all in that case. What hurts is returning big list in scalar context. How often does that happen? If it turns out to be a problem, then the next step it to return some wrapper object in scalar context instead. The wrapper will automatically stringify if used as a string, but you can still use it as a list if it is a list. I would not go to this complexity before it is proven that it is worth it. --Gisle |
From: Jeff H. <je...@ac...> - 2004-04-22 18:19:25
|
> my $mw = MainWindow->new; > my $label = $mw->Label(-text => "Hello")->pack; > if (my $file = $mw->getOpenFile) { > $label->configure(-text => "File [$file]"); ... > and then hit "Cancel" to the dialog that pops up, then we > fill in the field with something like "File > [ARRAY(0x8141ad8)]". Apparently tk_getOpenFile returns a > list instead of string, but this is not documented. Since I have identified the source of this problem, and it has to do with promiscuous object literal sharing in Tcl. The empty object (string rep "") is being converted to a list at some point (perfectly valid, as you can still request the string). The problem is that when the literal "" object is passed through to Perl, I check for tclListType first and create an (empty) array. My solution to this is to not create an array until the list actually has length > 0. This is all in SvFromTclObj, BTW. My question is, should I create the alternate "empty" list as newSV(0) (== undef), or newSVpvn("", 0) (== "" I think)? BTW, the messageBox issue was the same - the literal "yes" was being used as a boolean (which it validly is), and I was checking for boolean type without regard to the fact that the string rep of a boolean can be lots of things other than 0/1. The solution there is to make sure that I only convert booleans with no string rep to boolSVs. Jeff |
From: Gisle A. <gi...@Ac...> - 2004-04-22 20:58:11
|
"Jeff Hobbs" <je...@Ac...> writes: > My question is, should I create the alternate "empty" list > as newSV(0) (== undef), or newSVpvn("", 0) (== "" I think)? newSVpvn("", 0) is the right choice. Does tcl have a concept similar to undef? --Gisle |
From: Jeff H. <je...@ac...> - 2004-04-22 21:19:34
|
> "Jeff Hobbs" <je...@Ac...> writes: > > > My question is, should I create the alternate "empty" list > > as newSV(0) (== undef), or newSVpvn("", 0) (== "" I think)? > > newSVpvn("", 0) is the right choice. Does tcl have a concept > similar to undef? For the general case, no. Jeff |