Re: RFC 200 (v1) Objects: Revamp tie to support extensibility (Massive tie changes)

2000-09-10 Thread Michael G Schwern

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)

2000-09-10 Thread Nathan Wiger

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)

2000-09-10 Thread Michael G Schwern

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)

2000-09-10 Thread Michael G Schwern

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)

2000-09-10 Thread Nathan Wiger

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)

2000-09-10 Thread Damian Conway

 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)

2000-09-10 Thread Michael G Schwern

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)

2000-09-08 Thread Michael Fowler

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)

2000-09-07 Thread Perl6 RFC Librarian

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)

2000-09-07 Thread Michael G Schwern

[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