The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

PDLx::DetachedObject - parent class for subclassing PDL 2.X from class frameworks

VERSION

version 0.02

SYNOPSIS

Moo

   # DEPRECATED; use MooX::PDL2 instead
   package MyPDL;

   use Moo;
   use PDL::Lite;

   extends 'PDLx::DetachedObject';

   has PDL => ( is => 'rw' );

Class::Tiny

    package MyPDL;

    use Class::Tiny qw[ PDL ];

    use parent 'PDLx::DetachedObject';

Object::Tiny

    package MyPDL;

    use Object::Tiny qw[ PDL ];

    use parent 'PDLx::DetachedObject';

Class::Accessor

    package MyPDL;

    use parent 'Class::Accessor', 'PDLx::DetachedObject';
    __PACKAGE__->follow_best_practice;
    __PACKAGE__->mk_accessors( 'PDL' );

or with Antlers:

    package MyPDL;
    use Class::Accessor "antlers";
    use parent 'PDLx::DetachedObject';

    has PDL => ( is => 'ro' );

DESCRIPTION

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.

BUGS AND LIMITATIONS

Please report any bugs or feature requests to bug-pdlx-detachedobject@rt.cpan.org, or through the web interface at http://rt.cpan.org/Public/Dist/Display.html?Name=PDLx-DetachedObject.

AUTHOR

Diab Jerius <djerius@cpan.org>

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