in reply to Agile syntax (abuse?)

You wrote:

Thus what I'd want would rather be a set of especially named methods such as

sub AS_ARRAY { my $self=shift; # do something useful here }
so that
$obj->[42]; # would be the same as $obj->AS_ARRAY(42); # and so on...
But... that's exactly what arrayref-overloading does. I believe you can base your matrix class entirely on that. You'd access a matrix object $m like this
@$m # entire matrix, your syntax: $m->() $m->[ 1] # second row, your syntax $m->( 1); $m->[ 1]->[ 0] # individual element, your syntax $m->( 1, 0);
The syntax is pure Perl. Your's is more compact and closer to conventional mathematical notation. In fact, I like it and will keep it in mind for future matrix work.

Later you say:

...which is based, obviously, on blessed subrefs and also takes an Inside-Out approach: the object does contain something, namely a reference to itself, which also builds a closure, but is still used only like an index for property access.

Boo! That spoils most of the inside-out fun. Your objects rely on their body being a coderef, never mind how you access properties. Your class is hybrid to begin with and can't easily accomodate (inheritance-wise) a foereign class.

And it's not necessary either. Subref overloading is powerful enough to make your objects act as if their body were a coderef without it actually being one.

I have re-worked your class to be a true inside-out class without changing its interface or, I believe, its behavior. You may still prefer the more perlish style direct array overloading gives you and rewrite it that way.

I have also simplified your overload instructions somewhat.
package Matrix; use strict; use warnings; use Scalar::Util qw/refaddr/; use Data::Dumper; use base 'Exporter'; our @EXPORT='matrix'; use overload ( '""' => 'stringify', '*' => 'mult', '*=' => 'multby', '**' => 'pow', '&{}' => '_code', ); sub matrix { Matrix->new(@_) } sub mkaoa { [[ @_[0,1] ], [ @_[2,3] ]] } { my %entry; my %code; sub entry : lvalue { my $id=refaddr shift; @_==0 ? $entry{$id} : @_==1 ? $entry{$id}[ $_[0] ] : $entry{$id}[ $_[0] ][ $_[1] ]; } sub _code : lvalue { $code{ refaddr shift} } sub DESTROY { my $id = refaddr shift; delete $entry{ $id}; delete $code{ $id}; } } sub new { my $class = shift; my $self = bless( \ my $o, $class); $self->init(@_); } sub init { my $obj=shift; $obj->_code = sub : lvalue { $obj->entry(@_); }; $obj->() = mkaoa @_; $obj; } sub stringify { my $m = shift; "[[@{ $m->( 0) }] [@{ $m->( 1) }]]"; } sub _mult { my ($s,$o)=@_; my $code = $s->_code; $s->(0,0)*$o->(0,0) + $s->(0,1)*$o->(1,0), $s->(0,0)*$o->(0,1) + $s->(0,1)*$o->(1,1), $s->(1,0)*$o->(0,0) + $s->(1,1)*$o->(1,0), $s->(1,0)*$o->(0,1) + $s->(1,1)*$o->(1,1); } sub mult { my ($s,$o)=@_; matrix $s->_mult($o) } sub multby { my ($s,$o)=@_; $s->() = mkaoa $s->_mult($o); $s; } sub pow { my ($self, $n) = @_; my $out = matrix 1,0,0,1; if ( $n < 6 ) { $out->multby( $self) for 1 .. $n } else { my $pow = matrix map @$_, @{ $self->() }; # clone $self while ( $n ) { $out->multby( $pow) if $n & 1; $pow->multby( $pow); $n = int $n/2; } } $out; } 1; __END__
Anno

Update: Replaced the Matrix code. Essential change: ->init now does a full initialization instead of leaving part of the job to ->new. Inessential: Replaced ->pow with a more efficient implementation. Also added a stringification method

Replies are listed 'Best First'.
Re^2: Agile syntax (abuse?)
by blazar (Canon) on Apr 04, 2007 at 10:48 UTC
    But... that's exactly what arrayref-overloading does. I believe you can base your matrix class entirely on that. You'd access a matrix object $m like this

    Yes, yes, yes, diotalevi pointed that out already, I updated the root node, and I'm still D'oh'ing.

    The syntax is pure Perl. Your's is more compact and closer to conventional mathematical notation. In fact, I like it and will keep it in mind for future matrix work.

    Whoa! Although that was somewhat a minor detail in my post, I'm happy that something good is getting out of it and I feel honoured, and I'm not joking!

    It's really all about me being a nut for not having much experience with overload, reading its documentation quickly and making a wrong assumption. Had I read all of it, I would have of course known better.

    Boo! That spoils most of the inside-out fun. Your objects rely on their body being a coderef, never mind how you access properties. Your class is hybrid to begin with and can't easily accomodate (inheritance-wise) a foereign class.

    No, Anno, why do you say so? My objects IMHO do not rely on being coderefs. The implementation of some methods does, but as an option. One should not care about that and only be concerned about the interface anyway, shouldn't she? Indeed the coderef thingy only provides a shortcut that can, not that must, be used. Granted, sub-deref overloading is a much better means to obtain the same shortcut. (If the shortcut itself is part of the interface as opposed to an implementation detail, I suppose using it in derived classes would be legitimate as well.) Anyway, can't you have a derived class with code such as the following?

      Anyway, can't you have a derived class with code such as the following?
      # -*- Perl -*- use strict; use warnings; package Matrix::Named; use Scalar::Util qw/refaddr/; use base 'Matrix'; { my %name;
      Ha! Yes, that works because you have been careful to implement the name field in inside-out manner. The trouble begins when you want to combine the Matrix class with a foreign (non-inside-out) class.

      Say I've picked up your nifty Matrix class from somewhere, and this this lovely Angle class from somewhere else. I want to combine them in a class MyAngle so that matrix operations can be applied to an angle and refer to the corresponding rotational matrix.

      Unfortunately, Angle is implemented in the traditional way as a (scalar) ref to the numeric value.

      With a true inside-out implementation of Matrix, this can be easily done like this:
      #!/usr/local/bin/perl use strict; use warnings; $| = 1; use Vi::QuickFix; # Definition of MyAngle below my $alpha = MyAngle->new( rad => atan2( 1, 1)); printf "Angle: %s (%s deg)\n", $alpha->rad, $alpha->deg; # Angle func +tions print "Matrix:\n$alpha\n"; # show the matrix representation ################################################## package MyAngle; use lib 'lib'; use base 'Angle'; use base 'Matrix'; sub new { my $class = shift; $class->Angle::cre->MyAngle::init( @_); } sub init { my $obj = shift; $obj->Angle::init( @_); $obj->Matrix::init( cos( $_), -sin( $_), sin( $_), cos( $_)) for $obj->rad; $obj; } __END__
      This relies on the possibility to replace the body of an inside-out object, (normally an undefined scalar) with some foreign object of any provenience, (also a scalar in this case, but with significant content) so that its methods work without a hitch. This approach won't work with a hybrid class where the body is required to be code.

      Anno

      PS: If you actually want to run the code against the inside-out implementation of Matrix use the updated code from my previous posting. There was a bug in the original that would have been a show-stopper.

        This relies on the possibility to replace the body of an inside-out object, (normally an undefined scalar) with some foreign object of any provenience, (also a scalar in this case, but with significant content) so that its methods work without a hitch. This approach won't work with a hybrid class where the body is required to be code.

        (++) x As::Much->as('possible'); (which unfortunately is only 1) both for the intrinsic educational value of your post and for the nifty example you came up with. Perhaps all this may be the basis for a future tutorial... OTOH I must admit I'm such a blockhead that while I could understand at a glance why all this worked and grasped the beauty of the scheme, initially I could not see what would have gone wrong with "my" implementation, but eventually I did.

        Said this, I know I could find out by experimenting myself and reading the docs, but I seem to notice that "this kinda things" tends to present more possibile gotchas waiting in the shadow to bite you in the neck than one on average would expect, so I'm asking directly: if the implementation is a pure Inside-Out ref-agnostic one and deref overloading is used as in the above, can one reliably use dereferencing "as a shortcut" (supposing that in a realistic case that would be part of a documented public interface) in the derived class implementation?