PDLx::DetachedObject

PDLx::DetachedObject provides a minimal shim between PDL and
object-orientation frameworks. Directly subclassing PDL is tricky, as a
PDL object (a piddle) is a blessed scalar, not a blessed hash. PDL
provides an alternate means of subclassing; this class encapsulates that
prescription.

For Moo based classes, see MooX::PDL2, which provides a more integrated
approach.

  Background

Because a PDL object is a blessed scalar, outside of using inside-out
classes as the subclass, there is no easy means of adding extra
attributes to the object.

To work around this, PDL will treat any hash blessed into a subclass of
PDL which has an entry with key "PDL" whose value is a real PDL object
as a PDL object.

So far, here's a Moo version of the class

   package MyPDL;

   use Moo;

   extends 'PDL';

   # don't pass any constructor arguments to PDL->new; it confuses it
   sub FOREIGNBUILDARGS {}

   has PDL => ( is => 'rw' );
   has required_attr => ( is => 'ro', required =>1 );

When PDL needs to instantiate an object from the subclass, it doesn't
call the subclass's constructor, rather it calls the initialize class
method, which is expected to return a hash, blessed into the subclass,
containing the "PDL" key as well as any other attributes.

  sub initialize {
    my $class = shift;
    bless { PDL => PDL->null }, $class;
  }

The initialize method is invoked in a variety of places. For instance,
it's called in PDL::new, which due to Moo's inheritance scheme will be
called by MyPDL's constructor:

  $mypdl = MyPDL->new( required_attr => 2 );

It's also called when PDL needs to create an object to receive the
results of a PDL operation on a MyPDL object:

  $newpdl = $mypdl + 1;

There's one wrinkle, however. PDL *must* create an object without any
extra attributes (it cannot know which values to give them) so
initialize() is called with a *single* argument, the class name. This
means that $newpdl will be an *incomplete* MyPDL object, i.e.
"required_attr" is uninitialized. This can *really* confuse polymorphic
code which operates differently when handed a PDL or MyPDL object.

One way out of this dilemma is to have PDL create a *normal* piddle
instead of a MyPDL object. MyPDL has explicitly indicated it wants to be
treated as a normal piddle in PDL operations (by subclassing from PDL)
so this doesn't break that contract.

  $newpdl = $mypdl + 1;

would result in $newpdl being a normal PDL object, not a MyPDL object.

Subclassing from PDLx::DetachedObject effects this behavior.
PDLx::DetachedObject provides a wrapper constructor and an initialize
class method. The constructor ensures returns a properly subclassed hash
with the "PDL" key, keeping PDL happy. When PDL calls the initialize
function it gets a normal PDL.

  Classes without required constructor parameters

If your class does *not* require parameters be passed to the
constructor, it is safe to overload the "initialize" method to return a
fully fledged instance of your class:

 sub initialize { shift->new() }

  Using with Class Frameworks

The "SYNOPSIS" shows how to use PDLx::DetachedObject with various class
frameworks. The key differentiation between frameworks is whether or not
they will call a superclass's constructor. Moo always calls it,
Class::Tiny calls it only if it inherits from Class::Tiny::Object, and
Object::Tiny and Class::Accessor never will call the superclass'
constructor.

INSTALLATION

This is a Perl module distribution. It should be installed with whichever
tool you use to manage your installation of Perl, e.g. any of

  cpanm .
  cpan  .
  cpanp -i .

Consult http://www.cpan.org/modules/INSTALL.html for further instruction.
Should you wish to install this module manually, the procedure is

  perl Makefile.PL
  make
  make test
  make install

COPYRIGHT AND LICENSE

This software is Copyright (c) 2016 by Smithsonian Astrophysical
Observatory.

This is free software, licensed under:

  The GNU General Public License, Version 3, June 2007
