Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Creating a Moose object and serializing it

by daverave (Scribe)
on Oct 23, 2010 at 00:42 UTC ( [id://866908]=perlquestion: print w/replies, xml ) Need Help??

daverave has asked for the wisdom of the Perl Monks concerning the following question:

Disclosure: This question refers to a couple of previous questions posted at SO.

I am writing a module for a Moose object intended to be serialized. The module contains a method which implementation depends on one of the attributes the object is initialized with.

One approach would be to write this method=d with an `if`, so each time the method is called it will decide which of the implementations it should follow. However, since after the object has been initialized, the method always works the same it seems to me it might be more elegant to make this decision only once, after initialization, and skip the if. It should be noted that the method I discuss is invoked intensively, so perhaps it would also be notably more efficient to skip the if.

This is t what I came up with:

package MyObj 0.001; use 5.012; use Moose; use namespace::autoclean; has 'length' => ( is => 'ro', isa => 'Int', required => 1, ); has 'is_circular' => ( is => 'ro', isa => 'Bool', required => 1, ); has '_offset_sub' => ( is => 'ro', isa => 'CodeRef', lazy => 1, builder => '_build_offset_sub', init_arg => undef, ); sub offset () { my ( $self, $current_coord, $offset ) = @_; return $self->_offset_sub()->( $self, $current_coord, $offset ); } sub _build_offset_sub { my ($self) = @_; if ( $self->is_circular() ) { # circular return sub { my ( $self, $current_coord, $offset ) = @_; return ( $current_coord - 1 + $self->length() + $offset ) +% $self->length() + 1; }; } else { # linear return sub { my ( $self, $current_coord, $offset ) = @_; return $current_coord + $offset; }; } } __PACKAGE__->meta->make_immutable; 1;
I kind'a like this solution, but the problem is it uses coderef, which makes the object more difficult to serialize. I usually use `Storable`, but it seems not to like coderefs.

I actually have two questions:

1. Do you find this coderef-using solution appropriate? Do you have any other ideas for coding the body of a method after object initialization? Perhaps this is all a huge overkill over a single `if`?

2. How would you recommend serializing Moose objects in general and this object in particular? I've been looking into `MooseX::Storage` but didn't find it very helpful. Specifically, it doesn't support coderefs.

Thanks,
Dave.

Replies are listed 'Best First'.
Re: Creating a Moose object and serializing it
by kcott (Archbishop) on Oct 23, 2010 at 09:56 UTC
    I usually use `Storable`, but it seems not to like coderefs.

    I was doing some tests with Storable and coderefs a couple of weeks ago. The details are under the CODE REFERENCES section (not surprisingly :-) and begin "Since Storable version 2.05, ..." so you might want to check the version you're running.

    My use statement looks like this:

    use Storable qw{nstore retrieve}; { no warnings qw{once}; $Storable::Deparse = 1; $Storable::Eval = 1; }

    I wasn't using Moose but I was storing blessed objects containing coderefs.

    Anyway, if you do decide to take the Storable route, that may help.

    -- Ken

      Thanks Ken, that was helpful!

      Could you please explain the purpose of blocking the statements following `use`?

        Near the beginning of the script I have all warnings switched on:

        use warnings;

        The warnings pragma is lexically scoped and so affects the entire script.

        When I first started to test Storable with coderefs, my code looked like this:

        use Storable qw{nstore retrieve}; $Storable::Deparse = 1; $Storable::Eval = 1;

        Perl emitted a warning that each of those variables had only been used once and this possibly indicated an error in my code. I knew there wasn't an error and I didn't want this warning to be constantly displayed. I could have switched the warning off like this

        use Storable qw{nstore retrieve}; no warnings q{once}; $Storable::Deparse = 1; $Storable::Eval = 1;

        but that would have turned off the warning for the entire script: maybe later in the script there was a real error which now wouldn't be reported.

        By using a block I constrain the scope of the warning suppression to just that block. Furthermore, it also highlights what has been done. The following is equivalent but uses more code and is less obvious:

        use Storable qw{nstore retrieve}; no warnings q{once}; $Storable::Deparse = 1; $Storable::Eval = 1; use warnings q{once};

        Compare that with the block form:

        use Storable qw{nstore retrieve}; { no warnings qw{once}; $Storable::Deparse = 1; $Storable::Eval = 1; }

        -- Ken

Re: Creating a Moose object and serializing it
by mr_mischief (Monsignor) on Oct 23, 2010 at 02:09 UTC

    I may sound like broken record for saying this since it is common advice, but it's good advice so I just have to ask. What is your specific performance issue with the if statement in question, and what profiler data do you have to back up that assertion?

    Many optimization problems over which people agonize, plan, and scheme aren't really problems at all. If serialization is important, I can't imagine what performance degradation a simple conditional is causing that would be worth a bunch of extra code to do the serialization. I could see that degradation in a hot spot of the code, though, if a profiler actually showed it.

      I don't have any performance issues with the if. I didn't actually check it. I thought it would be more elegant to skip this check-every-time-the-method-is-invoked and I had no idea it will complicate things (it seemed quite simple).

      I might very well stay with the `if`, but now I'm curious in general as to how to do this stuff.

Re: Creating a Moose object and serializing it
by mojotoad (Monsignor) on Oct 23, 2010 at 06:09 UTC
    Smells like you're actually after a caching mechanism for is_circular().

    How often is circularity likely to change? Maybe you need something like has_been_circular().

    If it doesn't change, then just provide separate objects for circles and lines. What is it you're trying to do, anyway?

    Cheers,
    Matt

      Circularity never changes. I'm taking a look at `Moose::Manual::Roles` and `MooseX::AbstractFactory` to see if I can perhaps create a single interface with two implementations (sorry for the Java terms).
Re: Creating a Moose object and serializing it
by duelafn (Parson) on Oct 24, 2010 at 04:48 UTC

    I definitely think you are over-engineering this problem. I would consider an "if" in offset much simpler compared to the conceptual complexity of your coderef solution - much less thought necessary when reading your code. The coderef solution introduces the serialization problem. And additionally, you have replaced your "if" statement with a method call (_offset_sub), so you have made performance worse!

    I can't see an advantage to the coderef solution (in this case).

    To your real question: If the code differences between the circular and non-circular case were extensive, I might consider distinct classes (MyObj::Circular and MyObj::NonCircular with a common base class and where MyObj delegates/re-blesses to the apropriate subclass).

    Good Day,
        Dean

Re: Creating a Moose object and serializing it
by sundialsvc4 (Abbot) on Oct 25, 2010 at 13:23 UTC

    It intuitively seems to me that “a Moose object” is too much to be serializing.   What you really want to serialize, I would think, is a portion of the data associated with the object.   Undoubtedly there is a Moose appendage designed to do just that:   find it and use it.

    If you try to serialize “an object,” (i.e. “functional pieces and all”), you might wind up with a stored object that you one day cannot restore, because the underlying implementation somewhere in Moose (or in something you have built) has changed. When you try to restore the whole thing, what is resurrected, right before your eyes, is a ghost from the past ... no longer compatible with the present.

    Conceptually, I would think you would want (a fairly base-class) to provide methods such as, say, freeze_into(streamname) and thaw_from.   You instantiate the object, then you tell it to thaw-from some persistent data store.   Later, before destroying the object or allowing it to perish, you tell it to freeze-into.   In this way, the object is clearly differentiated from the state-data.   (And, you have a clearly-defined error path that you can take if you discover that the saved data is no longer compatible with the object.)   For most of the application, this would be something that “Just Works.™” ... a bit of magick that every object knows how to do.

      It intuitively seems to me that “a Moose object” is too much to be serializing.

      Actually, your intuition is wrong, a "Moose object" is just regular old HASH reference, just like any other plain old Perl object you might create. There is no magic involved there, in fact it is pretty boring.

      The only time Moose will add anything else to an instance, is if you start doing fancy stuff like applying roles to an instance, in which case it needs to keep a local reference to the metaclass and that can get ugly.

      -stvn

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://866908]
Approved by mr_mischief
Front-paged by mr_mischief
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (4)
As of 2024-03-28 22:26 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found