Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
So I thought of a serious problem with part of this RFC. The bit about using indirect object syntax... Instead, this RFC proposes that Ctie's operation become much more fundamental, simply translating functions via the existing indirect object syntax: tie Transaction %trans;# indirect object constructor $trans{$var} = $value ;# $obj-STORE($var, $value); lock $trans{$var}; # $obj-lock($var); There's a nasty inconsistency there. Consider the following. package Foo; sub lock { flock $_[0], LOCK_EX; } lock $trans{$var}; Normally, the Foo::lock() subroutine in the current package will be called. However, if %trans is a tied hash to a class which defines a lock() method (let's call it Lock::Ness) the meaning of the program radically and unexpectedly changes. Instead of CFoo::lock($trans{$var}), we now have C$obj-lock($var) and the two routines can do something *completely* different. This might have been your intention, to override the lock(0 function, but it becomes extremely dangerous when you consider the effect of a tied variable on interal code. You pass in a tied hash to a module and somewhere deep in its guts it calls the function foo() on it, but you have foo() defined as a method and your foo() has absolutly nothing to do with their foo() causing havoc. Action at a distance. The only possible recovery I can see is for the function currently in scope to have a higher "precedence" than the tied method. So in our example, the call to lock() remains a call to Foo::lock() and not to the tied method. The reverse situation, that of a tied method being accidentally turned into a function call, shouldn't happen in normal circumstances. Only problem is, reversing the precedence makes it near worthless. Read on. I just thought of a more fundemental problem. Looking at the rationale for this... Plus, if you want to support extra methods of your own, you must mix object and tied calls: $obj = tie %trans, 'Transaction'; $trans{$var} = $value; $obj-lock($var); Unfortunately, this defeats one of the key purposes of tie, which is OO transparency. And, creating a class that supports both OO and tied interfaces is difficult, requiring typeglobs or duplicate handler functions. Instead, this RFC proposes that Ctie's operation become much more fundamental, simply translating functions via the existing indirect object syntax: tie Transaction %trans;# indirect object constructor $trans{$var} = $value ;# $obj-STORE($var, $value); lock $trans{$var}; # $obj-lock($var); delete $trans{$var}; # $obj-delete($stuff); foo %trans;# $obj-foo; The idea is that by using the indirect object syntax, the tied variable can call methods on itself beyond those which are built in. But this doesn't really win you much besides some syntactic sugar. Consider this code: lock $trans{$var}; What if %trans is not tied? Then a lock() subroutine would have to be in scope, which presumably you've exported as part of your tied module. But that means you have to write function wrappers around your methods, which is even more work than the simple method aliasing for the OO/tie hybrid. And in the case of locking a key in a hash, you can't do it because lock() will receive the value. If the code is intended to be run *only* on tied variables, then you could have just used a plain object and dropped the sugar. -- Michael G Schwern http://www.pobox.com/~schwern/ [EMAIL PROTECTED] Just Another Stupid Consultant Perl6 Kwalitee Ashuranse Plus I remember being impressed with Ada because you could write an infinite loop without a faked up condition. The idea being that in Ada the typical infinite loop would be normally be terminated by detonation. -- Larry Wall in [EMAIL PROTECTED]
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
Michael Fowler wrote: =head3 Merge CTIESCALAR, CTIEHASH, and CTIEARRAY into CTIE I'm not so sure about this. I'm not either anymore. This will probably be removed from the next version. Instead, this RFC proposes that Ctie's operation become much more fundamental, simply translating functions via the existing indirect object syntax: tie Transaction %trans;# indirect object constructor $trans{$var} = $value ;# $obj-STORE($var, $value); lock $trans{$var}; # $obj-lock($var); Ignoring for a moment the ambiguities inherent in indirect object syntax (which we should all be aware of), what says $trans{$var} is not an object in itself, on which we want to call the lock method? Well, currently indirect objects would not be called in the syntax above. You would have to write: lock {$trans{$var}}; Because an indirect object can only be a scalar, block, or bareword. So I would say keeping a similar idea in place - if you want to use the object method instead of the tied method, use braces - might be the best way to disambiguate these. And remember you can also do this: $trans{$var}-lock If you want to make sure that the call is completely unambiguous. However, one thing RFC 174 tries to accomplish is getting Perl to NOT need the braces in these circumstances, which opens this whole can of worms right back up. My current thinking is precedence rules: 1. tied object 2. indirect object 3. package function 4. CORE function You could then use braces or - if you wanted to force the indirect object, as the above examples show (and how currently Perl 5 works). I think this is actually the behavior you'd want. For example, imagine a tied class whose purpose is to maintain filesystem objects. You'd want the tied calls to take precedence, since you might have a special way of sort'ing or push'ing your objects around. And force-disambiguating them becomes pretty simple and consistent: lock $trans{$var}; # use precedence rules lock {$trans{$var}};# indirect object (like Perl 5) lock ($trans{$var});# package or CORE function This is actually pretty much how Perl 5 works already. The only thing we've added is that the tied check is done in a slightly different way. =head3 Cuntie should take all references out of scope I would argue that this should be handled with weak references, and not enforced by untie(). I don't necessarily want Perl deleting my object behind my back because I untied a variable. Yeah, I agree with you, and I suspect that this problem will be implicitly resolved by not having to mix object and tied calls. This'll get the axe too. -Nate
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
On Sun, Sep 10, 2000 at 04:00:30PM -0700, Nathan Wiger wrote: Normally, the Foo::lock() subroutine in the current package will be called. However, if %trans is a tied hash to a class which defines a lock() method (let's call it Lock::Ness) the meaning of the program radically and unexpectedly changes. That's true. However, this is actually no different than the situation currently with blessed objects. If the code was rewritten: sub lock { print "Hello!" } $trans = new Lock::Ness; # ;-) lock $trans; # $trans-lock The lock method from the $trans object would be called, even though there was a main::lock, because of the higher precedence and binding of indirect objects. That's not right. package Lock::Ness; sub lock { print "Nessie\n" } sub new { bless {} } package Foo; sub lock { print "Foo lock\n" } my $trans = Lock::Ness-new; lock $trans; Run it. The result is "Foo lock". That's the way it should be, for the same reasons I've already pointed out. You don't want adding a method to a class to suddenly alter the behavior of distant code. But this doesn't really win you much besides some syntactic sugar. Consider this code: lock $trans{$var}; What if %trans is not tied? Then a lock() subroutine would have to be in scope However, here in the tie case I think it actually gains you a lot more than just sugar. Without this facility, you have to mix OO and tied calls snip No, I'm saying that if you're solely expecting %trans to always be tied and lock() to always be called as a method (which you have to, else you'd have to have a lock() method and a lock() function as I pointed out), then you may as well just have designed your interface as straight OO in the first place. Tying just buys you some cute syntax in this case. arguments for the RFC sniped This is the problem that the RFC is attempting to solve. So in this case, the indirect object syntax actually buys a lot. Yes, it does help solve those problems. The dangling untie() will happen less frequently, and you can customize the hell out of your tied class. But you're trading those problems for a new set of ambiguities! PS Your reply to Fowler hasn't hit the archives yet, so I'll hold off on jumping on his points until then. -- Michael G Schwern http://www.pobox.com/~schwern/ [EMAIL PROTECTED] Just Another Stupid Consultant Perl6 Kwalitee Ashuranse GuRuThuG make a channel called Perl, and infest it with joking and funit doesnt make alot of sense.
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
On Mon, Sep 11, 2000 at 01:31:39PM +1100, Damian Conway wrote: Or, better still, pass a reference to the actual variable being tied. Good idea. Also notice that I suggested the TIE be called as a method, so that it can be inherited if necessary (maybe you had that idea already???) The tie *can* currently be inherited. package Tie; sub TIESCALAR { print "Tie::TIESCALAR\n" } package Foo; @ISA = qw(Tie); package Bar; tie $thingy, "Foo"' Calls Tie::TIESCALAR, as expected. -- Michael G Schwern http://www.pobox.com/~schwern/ [EMAIL PROTECTED] Just Another Stupid Consultant Perl6 Kwalitee Ashuranse My room-mate has root I must now be filled with paste Summers here are hard. -- ignatz
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
Michael G Schwern wrote: sub lock { print "Hello!" } $trans = new Lock::Ness; lock $trans; # $trans-lock That's not right. You're correct. Sorry for not double-checking my examples. the same reasons I've already pointed out. You don't want adding a method to a class to suddenly alter the behavior of distant code. Regardless of my huge error above, this doesn't change the fact that this is exactly what tie() does currently in Perl 5. That is: tie @a, 'Matrix'; push @a, $stuff; Now changes the meaning of push() in the current package. So the only real difference in this situation from a user standpoint is that "PUSH" becomes "push" per the RFC, plus they can now do this with any function they please. Yes, it does help solve those problems. The dangling untie() will happen less frequently, and you can customize the hell out of your tied class. But you're trading those problems for a new set of ambiguities! I think your concerns are worthwhile. However, I think the "ambiguities" part can be resolved through precedence rules and similar constructs. I think there are many more potential upsides than downsides Iassuming a careful implementation. By v2 I expect to have a considerably better implementation section, complete with examples, specific code, rules, and so forth that should help alleviate any potential ambiguities. Also, little attention has been paid to operator overloading, which is another huge deal. Per this RFC, we can now integrate operator overloading and higher-order variables like @ and %, meaning we can add custom matrix math classes, special %hashes that do neat stuff in string contexts, and more. -Nate
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
Also notice that I suggested the TIE be called as a method, so that it can be inherited if necessary (maybe you had that idea already???) The tie *can* currently be inherited. Yes, I was aware. It's just that you wrote: tie Some::Class $foo, @args; would produce: TIE('SCALAR', 'Some::Class', @args); and I was concerned that we were planning to remove the polymorphic dispatch. (You may have to cut me some slack over the next few days if I say stupid things or jump to unwarranted conclusions -- I am very ill and my brain is not functioning properly %-) Damian
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
On Sun, Sep 10, 2000 at 09:22:39PM -0700, Nathan Wiger wrote: Regardless of my huge error above, this doesn't change the fact that this is exactly what tie() does currently in Perl 5. That is: tie @a, 'Matrix'; push @a, $stuff; Now changes the meaning of push() in the current package. So the only real difference in this situation from a user standpoint is that "PUSH" becomes "push" per the RFC, plus they can now do this with any function they please. Things like push(), exists(), etc... all have well defined meanings. If you add a push() method to your tied class its going to be for putting stuff onto the end of a list. Methods which override built-in functions are obvious enough. However, anything which is *not* a built-in, may only share its name and nothing else. use Some::Module qw(do_something); tie @a, 'Matrix; do_something @a; Harmless enough. But what happens if do_something() looks like this: sub do_something { my @a = @_; add @a, qw(some stuff); } If Matrix has an add() method, then it will interfere with Some::Module::add() and cause do_something() to do something really, really different than what was expected. The problem is that Matrix::add() and Some::Module::add() are related by name *only* and are not interchangable. The danger is of methods unintentionally being called in place of unrelated functions. -- Michael G Schwern http://www.pobox.com/~schwern/ [EMAIL PROTECTED] Just Another Stupid Consultant Perl6 Kwalitee Ashuranse MORONS!
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
On Fri, Sep 08, 2000 at 04:57:46AM -, Perl6 RFC Librarian wrote: =head3 Merge CTIESCALAR, CTIEHASH, and CTIEARRAY into CTIE In practice, people rarely make a class that Cties multiple data types through the same interface. The reason is that CSTORE, CFETCH, CDESTROY, and other methods overlap. As such, it is more feasible to create several different modules; witness CTie::Array, CTie::Scalar, CApache::Session, and other modules. I'm not so sure about this. As Schwern mentioned, it'd be nice to know what exactly is being tied, and he suggested a first argument of the type. But isn't this just moving the information from the subroutine name into @_? I'd much rather Perl say "oh, you can't tie a hash, there's no TIEHASH" than be forced to switch on the first argument and raise a fatal exception if I can't support what's being tied. Instead, this RFC proposes that Ctie's operation become much more fundamental, simply translating functions via the existing indirect object syntax: tie Transaction %trans;# indirect object constructor $trans{$var} = $value ;# $obj-STORE($var, $value); lock $trans{$var}; # $obj-lock($var); Ignoring for a moment the ambiguities inherent in indirect object syntax (which we should all be aware of), what says $trans{$var} is not an object in itself, on which we want to call the lock method? =head3 Cuntie should take all references out of scope When called, Cuntie currently suffers the somewhat nasty problem of not being able to automatically destroy inner references. This means if you've mixed OO and Ctied calls, you may not be able to destroy your tied object as easily as you like. Cuntie should forceably destroy hanging references. [2] I would argue that this should be handled with weak references, and not enforced by untie(). I don't necessarily want Perl deleting my object behind my back because I untied a variable. Michael -- Administrator www.shoebox.net Programmer, System Administrator www.gallanttech.com --
RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
This and other RFCs are available on the web at http://dev.perl.org/rfc/ =head1 TITLE Objects: Revamp tie to support extensibility (Massive tie changes) =head1 VERSION Maintainer: Nathan Wiger [EMAIL PROTECTED] Date: 07 Sep 2000 Mailing List: [EMAIL PROTECTED] Version: 1 Number: 200 Status: Developing Requires: RFC 159, RFC 174 =head1 ABSTRACT Ctie is really cool. Mostly. It has an amazing amount of power in concept, but suffers from several limitations which this RFC attempts to address. =head1 DESCRIPTION =head2 Overview Many people have expressed problems with tie, including Larry [1]. Ctie suffers from several limitations: 1. It is non-extensible; you are limited to using functions that have been implemented with tie hooks in them already. 2. Any additional functions require mixed calls to tied and OO interfaces, defeating a chief goal: transparency. 3. It is slow (at least slower than other objects) 4. You can't easily integrate tie and operator overloading. 5. If defining tied and OO interfaces, you must define duplicate functions or use typeglobs. 6. Some parts of the syntax are, well, kludgey This RFC attempts to address all of these points with some changes to syntax and implementation concepts. It interacts with the concept of Bpolymorphic objects, described in LRFC 159, to provide a simple and extensible framework. =head2 New Concepts This RFC proposes two key principles that will provide a more general-purpose Ctie framework: 1. Operator, data, and syntax overloading will be done via the ALLCAPS methods described in RFC 159. 2. All other functions are directly translated via the indirect object syntax. In addition, the declaration of a tie statement is suggested to be changed into a standard indirect object function: $object = tie Tie::Class @array_to_tie; The default Ctieing would be performed by CUNIVERSAL::tie, which would be a new method that properly "blessed" the tied variable and then simply turned around and called the class's CTIE method, similar to how the builtin Ctie works currently. There are many changes, so let's go through them one at a time and then revisit how they will all tie (ha-ha) together at the end. =head2 Syntax Changes =head3 Drop Ctie builtin and replace with CUNIVERSAL::tie As mentioned above, this allows us to call Ctie in a simple indirect object form. This eliminates one more special-case function which currently requires that quotes be placed around the class name. This syntax should simply be modified to be called on the object it will be tied to, since Ctie is after all an object constructor. =head3 Merge CTIESCALAR, CTIEHASH, and CTIEARRAY into CTIE In practice, people rarely make a class that Cties multiple data types through the same interface. The reason is that CSTORE, CFETCH, CDESTROY, and other methods overlap. As such, it is more feasible to create several different modules; witness CTie::Array, CTie::Scalar, CApache::Session, and other modules. =head3 Drop CTIEHANDLE Thanks to the below syntax, differentiating between filehandles and other scalars is no longer important. It would also be very difficult to make this distinction, since in Perl 6 filehandles are intended to be C$scalars. =head3 Continue to do data handling through ALLCAPS methods This will not change. CSTORE and CFETCH, along with other functions described in RFC 159 and below, will continue to do data handling. In addition, these methods will be used for operator overloading as well. =head3 Perform functions through the indirect object syntax Currently, Ctie incurs a fairly substantial overhead (depending on your application) because function calls such as this: push @tied_array, $value; Must be internally transformed into this: $self-PUSH($value); In addition, you are bound by the methods that the designers have implemented. While Perl 5.6 finally has a fairly substantial collection, nonetheless it is easy to imagine that future functions will arise which you want to Ctie, but which support has not been added for yet. Plus, if you want to support extra methods of your own, you must mix object and tied calls: $obj = tie %trans, 'Transaction'; $trans{$var} = $value; $obj-lock($var); Unfortunately, this defeats one of the key purposes of tie, which is OO transparency. And, creating a class that supports both OO and tied interfaces is difficult, requiring typeglobs or duplicate handler functions. Instead, this RFC proposes that Ctie's operation become much more fundamental, simply translating functions via the existing indirect object syntax: tie Transaction %trans;# indirect object constructor $trans{$var} = $value ;# $obj-STORE($var, $value); lock $trans{$var}; # $obj-lock($var); delete $trans{$var}; # $obj-delete($stuff); foo %trans;# $obj-foo; %trans = (); #
Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)
[I'll take off my black hat for a moment...] Okay, this is the FIRST TIME I've ever seen indirect object syntax used for anything useful. (That's praise, BTW) I was going to suggest that KEYS and VALUES methods be added to tied hashes, but this RFC makes it all moot. Well done. [Black hat back on.] On Fri, Sep 08, 2000 at 04:57:46AM -, Perl6 RFC Librarian wrote: 3. It is slow (at least slower than other objects) Its not slow. ITS SLOWER THAN MOLASSES ON A COLD DAY ON PLUTO! Simple benchmarking shows that tied access is nearly an order of magnitude slower than method access which is nearly an order of magnitude slower than normal hashes. #!/usr/bin/perl -w use Benchmark; use Tie::Hash; tie %tied, 'Tie::StdHash'; $tied{foo} = 'bar'; $hash{foo} = 'bar'; $obj = tied %tied; timethese(shift || -3, { tied = sub { $tied{foo} }, hash = sub { $hash{foo} }, method = sub { $obj-FETCH('foo') }, }); Benchmark: timing 20 iterations of hash, method, tied... hash: 1 wallclock secs ( 0.15 usr + -0.01 sys = 0.14 CPU) @ 1428571.43/s (n=20) (warning: too few iterations for a reliable count) method: 0 wallclock secs ( 0.81 usr + 0.00 sys = 0.81 CPU) @ 246913.58/s (n=20) tied: 5 wallclock secs ( 5.00 usr + 0.02 sys = 5.02 CPU) @ 39840.64/s (n=20) While the disparity between normal hashes and methods is acceptable (optimized C code vs Perl) that between tied variables and methods is not. Anyhow, this horse is long dead. =head3 Merge CTIESCALAR, CTIEHASH, and CTIEARRAY into CTIE In practice, people rarely make a class that Cties multiple data types through the same interface. The reason is that CSTORE, CFETCH, CDESTROY, and other methods overlap. As such, it is more feasible to create several different modules; witness CTie::Array, CTie::Scalar, CApache::Session, and other modules. I like this, but I can foresee a problem. If someone does: tie Some::Class $foo; but Some::Class is really for hashes, you won't know it until something else blows up. It may make sense to pass a leading argument to TIE which is the type of variable being tied. tie Some::Class $foo, @args; would produce: TIE('SCALAR', 'Some::Class', @args); BTW Should CSome::Class-tie($foo, @args) work or would it be Some::Class-TIE($foo, @args)? Or both? Or neither? =head3 Perform functions through the indirect object syntax Currently, Ctie incurs a fairly substantial overhead (depending on your application) because function calls such as this: push @tied_array, $value; Must be internally transformed into this: $self-PUSH($value); True, but I don't believe this is the real reason for the inefficiencies. You might want to ask on p5p for a detailed set of reasons why tying is inefficient. I think some of it has to do with oddities in the implementation. Also... Instead, this RFC proposes that Ctie's operation become much more fundamental, simply translating functions via the existing indirect object syntax: tie Transaction %trans;# indirect object constructor $trans{$var} = $value ;# $obj-STORE($var, $value); lock $trans{$var}; # $obj-lock($var); furthermore... In each of these examples, the variable that is being Ctied acts like little more than a syntactic mask over an object. Translation is potentially much faster because the indirect object syntax is used. No, a similar translation must be made. lock $trans{$var}; must still check to see if %trans is tied. If so it must retrieve that tied object and translate to the lock() method call. Little or no efficiencies will be had. (I'm not done yet...) # Include tied interface sub TIE { my $self = self;# RFC 152 :-) bless {@_}, $self; } Ya know, this almost points out a weakness in RFC 152. Why have a self keyword if you're immediately going to assign it to a variable??? Perhaps it was just from habit. Anyhow, this is not the place to bring that up as I just did. Actually, Ctie might be able to be handled completely by a preprocessor of sorts. Consider this code: tie My::Matrix @a, [ 1, 2, 3 ]; @a = @a + @b; # $obj-CLEAR; $obj-STORE($obj-ADD(@b)); @a = @a * @b; # $obj-CLEAR; $obj-STORE($obj-MUL(@b)); push @a, $value;# $obj-push($value); If Perl simply ran something like a Cs/\@a\b\s*,?/\$obj/g over the code internally, then all of this would become: $obj = My::Matrix-tie([1, 2, 3]); $obj = $obj + @b; # $obj-CLEAR; $obj-STORE($obj-ADD(@b)); $obj = $obj * @b; # $obj-CLEAR; $obj-STORE($obj-MUL(@b)); push $obj $value; # $obj-push($value); Can't be done. Consider: my $foo; if( $something ) { tie Some::Class $foo; } print $foo; You can't