blazar has asked for the wisdom of the Perl Monks concerning the following question:
I would like to premise that I know what OOP is supposed to be for, but in some cases besides its "structural" benefits, it also makes for agile syntax, which IMHO is good. And the other day I felt like pushing the thing somewhat further...
Well, more precisely I was thinking of a situation in which the objects (in the IT acceptation) will represent mathematical objects (generic acceptation.) Think for example of 2x2 matrices: for one thing one may want to ease their construction. In this case it is possible to export the constructor: it's not usual, but then many OO modules are used just instantiating one object at a time, and in this case it may be appropriate, instead.
OTOH, one may also want a very quick access to the individual entries. In this case the natural choice would seem to use a blessed AoA reference to implement these objects. Yet this has all the usual problems of "traditional" objects, including those with inheritance, and I may well want to subclass a matrix class. Basically I would like an agile notation as the one I would get knowing the implementation details, but I also want to avoid to do so at the same time.
Expanding on the last paragraph above, ideally what I'd like would be a means to dereference an object in some specific way regardless of the object's implementation itself. In this sense I see that overload can overload dereferencing, but only for the ${} @{} %{} &{} *{} forms, and not for the arrow operator. Thus what I'd want would rather be a set of especially named methods such as
Update: as pointed out by diotalevi, that is not the case, hence the striked out text. (/me goes thinkering whether the approach I presented here may have some other point of interest, apart that of putting something "exotic" in the blessed ref used for object implementation...)
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...
Unfortunately, such beasts do not exist. Anyway sub dereferencing is agile enough for me and all of what I need. Thus I also tried putting a suitable sub in the CODE slot of the '' stash entry, but not unexpectedly it doesn't work: when perl sees ->(, it reads "sub dereferencing", not "call of the '' method"...
Well, I found a "solution" (which is a solution only in quotes because there's not much of an actual problem, just desire to explore syntax and semantics possibilities) 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.
Actually, had not been for a problem I'm reporting below, I would have not posted this under SoPW, but as a meditation, possibly under a title such as "blazar's bizarre OO model".
To explain the basic idea hereafter I'm showing what could be the skeleton of a Matrix class. Please note that this also uses lvalued methods which are a completely debatable, and debated, subject in and of themselves... (But if I'm to get on people's nerves, I want to do it thoroughly! :-)
# -*- Perl -*- use strict; use warnings; package Matrix; use Scalar::Util qw/refaddr/; use Data::Dumper; use base 'Exporter'; our @EXPORT='matrix'; use overload '*' => sub { $_[0]->mult($_[1]) }, '*=' => sub { $_[0]->multby($_[1]) }, '**' => sub { $_[0]->pow($_[1]) }; sub matrix { Matrix->new(@_) } sub mkaoa { [[ @_[0,1] ], [ @_[2,3] ]] } { my %entry; sub entry : lvalue { my $id=refaddr shift; @_==0 ? $entry{$id} : @_==1 ? $entry{$id}[ $_[0] ] : $entry{$id}[ $_[0] ][ $_[1] ]; } sub DESTROY { delete $entry{refaddr shift} } } sub new { my $class = shift; my $self; $self = sub : lvalue { $self->entry(@_) }; bless($self => $class)->init(@_); } sub init { my $obj=shift; $obj->() = mkaoa @_; $obj; } sub _mult { my ($s,$o)=@_; $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, $pow)=@_; my $out=matrix 1,0,0,1; $out->multby($self) for 1..$pow; $out; } 1; __END__
Note: I am also aware that this doesn't make for speed nor for memory-friendness either, but I don't care. I do care when... it is worthwile to care. I'm concerned with syntax here.
And you would use it like thus:
#!/usr/bin/perl -l use strict; use warnings; use Matrix; # A somewhat esoteric way to print the # twelfth Fibonacci number. print +(matrix(1,1,1,0)**12)->(1,0); __END__
There's an obvious problem with the approach above: the objects, as anonymous subs, hold a reference to themselves. Thus when you reach a point where a "normal" object would be DESTROYed, these won't because there's still a reference to them lying around. I'm showing this with a minimal example:
#!/usr/bin/perl use strict; use warnings; package Foo; use Scalar::Util qw/refaddr/; { my %bar; sub bar { my $id=refaddr shift; @_ ? $bar{$id}=shift : $bar{$id}; } sub DESTROY { my $obj=shift; warn "Deleting $obj\n"; delete $bar{refaddr $obj}; } } sub new { my ($class, $type, $data)=@_; my $obj; $obj=bless $type eq 'code' ? sub { $obj } : {} => $class; warn "Creating $obj\n"; $obj->bar($data); } package main; sub test { my $type=shift; warn "--- Testing <$type> ---\n"; my $x=Foo->new($type => 'x'); warn "Leaving context\n"; } test $_ for qw/code other/; END { warn "Leaving program\n" } __END__
This gives me:
--- Testing <code> --- Creating Foo=CODE(0x224eb4) Leaving context --- Testing <other> --- Creating Foo=HASH(0x225bec) Deleting Foo=HASH(0x225bec) Leaving context Leaving program Deleting Foo=CODE(0x224eb4)
Thus, with no interventions leakages are unavoidable. This is by all means undesirable.
I thought it would have been obvious that I could use Scalar::Util's weaken() as a cure, but I played around with it and cannot seem to make the approach to work. Am I missing something obvious? All of my idea may not be such a great contribution to humanity, after all, but I still find it interesting in principle, and I would like to know if at least it can be patched as to avoid that hateful leakage.
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Agile syntax (abuse?)
by diotalevi (Canon) on Apr 03, 2007 at 17:22 UTC | |
by blazar (Canon) on Apr 03, 2007 at 17:39 UTC | |
|
Re: Agile syntax (abuse?)
by Anno (Deacon) on Apr 03, 2007 at 18:51 UTC | |
by blazar (Canon) on Apr 04, 2007 at 10:48 UTC | |
by Anno (Deacon) on Apr 04, 2007 at 18:29 UTC | |
by blazar (Canon) on Apr 04, 2007 at 19:41 UTC | |
by Anno (Deacon) on Apr 05, 2007 at 18:24 UTC | |
|
Re: Agile syntax (abuse?)
by Rhandom (Curate) on Apr 03, 2007 at 20:33 UTC | |
by blazar (Canon) on Apr 04, 2007 at 11:08 UTC |