Overloaded <> and deref again
[p5sagit/p5-mst-13.2.git] / lib / overload.pm
index 43fef8a..81d9a12 100644 (file)
@@ -121,6 +121,8 @@ sub mycan {                         # Real can would leave stubs.
         mutators         => '++ --',
         func             => "atan2 cos sin exp abs log sqrt",
         conversion       => 'bool "" 0+',
+        iterators        => '<>',
+        dereferencing    => '${} @{} %{} &{} *{}',
         special          => 'nomethod fallback =');
 
 sub constant {
@@ -362,12 +364,29 @@ for "E<lt>" or "E<lt>=E<gt>" combined with either unary minus or subtraction.
 
     "bool", "\"\"", "0+",
 
-If one or two of these operations are unavailable, the remaining ones can
+If one or two of these operations are not overloaded, the remaining ones can
 be used instead.  C<bool> is used in the flow control operators
 (like C<while>) and for the ternary "C<?:>" operation.  These functions can
 return any arbitrary Perl value.  If the corresponding operation for this value
 is overloaded too, that operation will be called again with this value.
 
+=item * I<Iteration>
+
+    "<>"
+
+If not overloaded, the argument will be converted to a filehandle or
+glob (which may require a stringification).  The same overloading
+happens both for the I<read-filehandle> syntax C<E<lt>$varE<gt>> and
+I<globbing> syntax C<E<lt>${var}E<gt>>.
+
+=item * I<Dereferencing>
+
+    '${}', '@{}', '%{}', '&{}', '*{}'.
+
+If not overloaded, the argument will be dereferenced I<as is>, thus
+should be of correct type.  These functions should return a reference
+of correct type, or another object with overloaded dereferencing.
+
 =item * I<Special>
 
     "nomethod", "fallback", "=",
@@ -392,6 +411,8 @@ A computer-readable form of the above table is available in the hash
  mutators        => '++ --',
  func            => 'atan2 cos sin exp abs log sqrt',
  conversion      => 'bool "" 0+',
+ iterators       => '<>',
+ dereferencing   => '${} @{} %{} &{} *{}',
  special         => 'nomethod fallback ='
 
 =head2 Inheritance and overloading
@@ -589,6 +610,14 @@ C<E<lt>=E<gt>> or C<cmp>:
     <, >, <=, >=, ==, !=       in terms of <=>
     lt, gt, le, ge, eq, ne     in terms of cmp
 
+=item I<Iterator>
+
+    <>                         in terms of builtin operations
+
+=item I<Dereferencing>
+
+    ${} @{} %{} &{} *{}                in terms of builtin operations
+
 =item I<Copy operator>
 
 can be expressed in terms of an assignment to the dereferenced value, if this
@@ -851,6 +880,134 @@ numeric value.)  This prints:
   seven=vii, seven=7, eight=8
   seven contains `i'
 
+=head2 Two-face references
+
+Suppose you want to create an object which is accessible as both an
+array reference, and a hash reference, similar to the builtin
+L<array-accessible-as-a-hash|perlref/"Pseudo-hashes: Using an array as
+a hash"> builtin Perl type.  Let us make it better than the builtin
+type, there will be no restriction that you cannot use the index 0 of
+your array.
+
+  package two_refs;
+  use overload '%{}' => \&gethash, '@{}' => sub { $ {shift()} };
+  sub new { 
+    my $p = shift; 
+    bless \ [@_], $p;
+  }
+  sub gethash {
+    my %h;
+    my $self = shift;
+    tie %h, ref $self, $self;
+    \%h;
+  }
+
+  sub TIEHASH { my $p = shift; bless \ shift, $p }
+  my %fields;
+  my $i = 0;
+  $fields{$_} = $i++ foreach qw{zero one two three};
+  sub STORE { 
+    my $self = ${shift()};
+    my $key = $fields{shift()};
+    defined $key or die "Out of band access";
+    $$self->[$key] = shift;
+  }
+  sub FETCH { 
+    my $self = ${shift()};
+    my $key = $fields{shift()};
+    defined $key or die "Out of band access";
+    $$self->[$key];
+  }
+
+Now one can access an object using both the array and hash syntax:
+
+  my $bar = new two_refs 3,4,5,6;
+  $bar->[2] = 11;
+  $bar->{two} == 11 or die 'bad hash fetch';
+
+Note several important features of this example.  First of all, the
+I<actual> type of $bar is a scalar reference, and we do not overload
+the scalar dereference.  Thus we can get the I<actual> non-overloaded
+contents of $bar by just using C<$$bar> (what we do in functions which
+overload dereference).  Similarly, the object returned by the
+TIEHASH() method is a scalar reference.
+
+Second, we create a new tied hash each time the hash syntax is used.
+This allows us not to worry about a possibility of a reference loop,
+would would lead to a memory leak.
+
+Both these problems can be cured.  Say, if we want to overload hash
+dereference on a reference to an object which is I<implemented> as a
+hash itself, the only problem one has to circumvent is how to access
+this I<actual> hash (as opposed to the I<virtual> exhibited by
+overloaded dereference operator).  Here is one possible fetching routine:
+
+  sub access_hash {
+    my ($self, $key) = (shift, shift);
+    my $class = ref $self;
+    bless $self, 'overload::dummy'; # Disable overloading of %{} 
+    my $out = $self->{$key};
+    bless $self, $class;       # Restore overloading
+    $out;
+  }
+
+To move creation of the tied hash on each access, one may an extra
+level of indirection which allows a non-circular structure of references:
+
+  package two_refs1;
+  use overload '%{}' => sub { ${shift()}->[1] },
+               '@{}' => sub { ${shift()}->[0] };
+  sub new { 
+    my $p = shift; 
+    my $a = [@_];
+    my %h;
+    tie %h, $p, $a;
+    bless \ [$a, \%h], $p;
+  }
+  sub gethash {
+    my %h;
+    my $self = shift;
+    tie %h, ref $self, $self;
+    \%h;
+  }
+
+  sub TIEHASH { my $p = shift; bless \ shift, $p }
+  my %fields;
+  my $i = 0;
+  $fields{$_} = $i++ foreach qw{zero one two three};
+  sub STORE { 
+    my $a = ${shift()};
+    my $key = $fields{shift()};
+    defined $key or die "Out of band access";
+    $a->[$key] = shift;
+  }
+  sub FETCH { 
+    my $a = ${shift()};
+    my $key = $fields{shift()};
+    defined $key or die "Out of band access";
+    $a->[$key];
+  }
+
+Now if $baz is overloaded like this, then C<$bar> is a reference to a
+reference to the intermediate array, which keeps a reference to an
+actual array, and the access hash.  The tie()ing object for the access
+hash is also a reference to a reference to the actual array, so 
+
+=over
+
+=item *
+
+There are no loops of references.
+
+=item *
+
+Both "objects" which are blessed into the class C<two_refs1> are
+references to a reference to an array, thus references to a I<scalar>.
+Thus the accessor expression C<$$foo-E<gt>[$ind]> involves no
+overloaded operations.
+
+=back
+
 =head2 Symbolic calculator
 
 Put this in F<symbolic.pm> in your Perl library directory: