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

Howdy!

I'm building a class hierarchy using Object::InsideOut. Here's the problem package:

package Morsulus::Catalog::SetOf; use strict; use warnings; use Object::InsideOut qw/ Set::Scalar::Valued /; { my @the_sets :Field :Get('_get_the_set'); sub _init :Init { my $self = shift; my $args = shift; my $the_set = Set::Scalar::Valued->new(); $self->set(\@the_sets, $the_set); $self->inherit($the_set, $args); } } 1;
Here's the test:
use Test::More tests => 4; use Test::Exception; BEGIN { use_ok( 'Morsulus::Catalog::SetOf' ); } diag( "Testing Morsulus::Catalog::SetOf"); my $thingy; lives_ok { $thingy = Morsulus::Catalog::SetOf->new(); } 'created SetOf +'; ok $thingy->can('values'), '$thingy->can("values")'; ok $thingy->isa('Set::Scalar::Valued'), '$thingy is a Set::Scalar::Val +ued';
Here's the relevant output of the script:
t/00.setof....NOK 2/4 + # Failed test 'created SetOf' # at t/00.setof.t line 12. # died: OIO::Args error: Missing arg(s) to '->inherit()' # Package: Morsulus::Catalog::SetOf # File: blib/lib/Morsulus/Catalog/SetOf.pm # Line: 17 # # # Trace begun at blib/lib/Morsulus/Catalog/SetOf.pm line 17 # Object::InsideOut::new at t/00.setof.t line 12 # Test::Exception::lives_ok at t/00.setof.t line 12
I tried writing my _init subroutine using the POD as guidance, but that failed the same way. I could not discern anything constructive by examining the code. I don't know if this is a bug in Object::InsideOut or something simple I'm just missing. Can anyone help me figure out what is going wrong here?

yours,
Michael

Replies are listed 'Best First'.
Re: Difficulty with Object::InsideOut
by Anno (Deacon) on Mar 24, 2007 at 12:14 UTC
    Just for confirmation, I see the same test results when I run your code.

    However, taking a look at Object::InsideOut's documentation I noticed this:

    Object::InsideOut implements inside-out objects as anonymous scalar references that are blessed into a class with the scalar containing the ID for the object ...

    The underlined part implies that an object must access its body (the scalar ref) to get its id. This is something a real inside-out object never does. The salient point about the inside-out model is that nothing is stored in the object itself, so that the body is never accessed. This, and only this, gives inside-out classes the indepencence from object implementation that makes inheritance "just work", the way it does in dedicated OO languages.

    In other words, Object::InsideOut doesn't give you true inside-out objects. You're probably better off if you base your class on one of the other implementations of inside-out classes on CPAN (Class::InsideOut or Class::Std), or implement it directly.

    Anno

    Update:I erroneously accused Class::InsideOut of not giving you "true inside-out objects". Corrected to say Object::InsideOut. Class::InsideOut does produce true inside-out objects. Apologies to those involved, and thanks to shmem who caught the error.

    Update: Made links of some references to classes

      Howdy!

      Object::InsideOut uses lexical arrays to store attribute values, so it needs to store the array index somewhere. The "inside-outness" comes from the fact that even if you know the index, you can't get to the arrays to break encapsulation. That doesn't violate "inside-outness".

      I was trying to use the ability to have a class inherit from a foreign (non-Object::InsideOut) package. In this case, I'm wrapping a Set::Scalar::Valued object, and I want a Morsulus::Catalog::SetOf object to be able to inherit the methods from Set::Scalar::Valued. According to the docs for Object::InsideOut, I simply call the inherit method and pass it an instance of the foreign object. In my example, I'm also going so far as to keep that object as an attribute of my SetOf object.

      I elected to try Object::InsideOut because it offers a richer set of features than Class::InsideOut, and it's not doing things that Class::Std does that have bad implications (CHECK blocks come to mind as potentially problematic).

      Oh, and I looked at the test suite for Object::InsideOut to see how it tests the foreign inheritance functionality. I don't see the difference, and I can't discern just what additional arguments inherit is expecting.

      yours,
      Michael
        I was trying to use the ability to have a class inherit from a foreign (non-Object::InsideOut) package.

        I am not amazed that it is just this feature that is problematic in Object::InsideOut. Inheritance from a non-inside-out class is exactly where a full inside-out class exploits its agnosticism with respect to its body. Any actual object can be substituted and still allow access to the data that are implemented inside-out style. Once you use the body, for anything at all, this feature goes away. In the case of Object::InsideOut, the body must contain the id as a scalar, or behave as if it did. I don't know how Object::InsideOut solves the problem, but it can't be straightforward.

        The inside-out technique brings two things that are lacking in traditional Perl OOP: encapsulation and (almost) unimpeded inheritance. Of the two, the implicatrions on inheritance are far more important. Not because of any theoretical reasons, but because the lack of encapsulation hasn't had any serious consequences. People don't access the innards of an object directly, and that is that.

        The lack of free inheritance has hindered the development of OOP in Perl. You want to subclass an existing class from CPAN? Well, you can't without getting intimately involved in the implementation of the prospective superclass. If your class is inside-out, or if the superclass is, the problem goes away. That makes a big difference.

        There is deplorably little code reuse among the classes on the CPAN. When inside-out becomes more common, that could change.

        Anno

Re: Difficulty with Object::InsideOut
by herveus (Prior) on Mar 24, 2007 at 22:15 UTC
    Howdy!

    Well...that's interesting.

    I changed Set::Scalar::Valued to something else and it all worked fine. I tried two unrelated packages (CGI and Net::Server).

    In the debugger, I find more "odd" behavior. I think I may need to examine Set::Scalar::Valued to see why, in:

    # Flatten arg list my @arg_objs; while (my $arg = shift) { if (ref($arg) eq 'ARRAY') { push(@arg_objs, @{$arg}); } else { push(@arg_objs, $arg); } }
    (being in Object::InsideOut::Foreign::inherit) that stepping into "while (my $arg = shift)" takes me directly to Set::Scalar::Base::size with the object that shift() would have consumed being passed in.

    Ick. Examination of the entrails of Set::Scalar reveals nothing to me as yet. At this point, I'm puzzled more.

    yours,
    Michael
Re: Difficulty with Object::InsideOut
by herveus (Prior) on Mar 25, 2007 at 17:21 UTC
    Howdy!

    OK. Short answer: both guilty, sort of. Consider 'foo.pl':

    #!/usr/bin/perl use strict; use warnings; use Set::Scalar::Valued; test_args(Set::Scalar::Valued->new()); sub test_args { my @opt_args; while (my $arg = shift) { if (ref($arg) eq 'ARRAY') { push @opt_args, @$arg; } else { push @opt_args, $arg; } } print scalar(@opt_args), "\n"; }

    Now consider the following session via 'perl -d foo.pl':

    main::(foo.pl:6): test_args(Set::Scalar->new()); + DB<0> s Set::Scalar::Base::new(/Library/Perl/5.8.6/Set/Scalar/Base.pm:68): 68: my $class = shift; + DB<0> r list context return from Set::Scalar::Base::new: 0 Set::Scalar=HASH(0x18092cc) 'elements' => HASH(0x194c7d4) empty hash 'universe' => Set::Scalar::Universe=HASH(0x18d312c) 'elements' => HASH(0x18b1db0) empty hash 'null' => Set::Scalar::Null=HASH(0x18acce4) 'universe' => Set::Scalar::Universe=HASH(0x18d312c) -> REUSED_ADDRESS 'universe' => undef main::test_args(foo.pl:10): my @opt_args; + DB<0> n main::test_args(foo.pl:11): while (my $arg = shift) main::test_args(foo.pl:12): { + DB<0> s Set::Scalar::Base::size(/Library/Perl/5.8.6/Set/Scalar/Base.pm:122): 122: my $self = shift;

    I'm now puzzling over just how one goes from "my $arg = shift" to Set::Scalar::Base::size... and I return from a bit more digging.

    Set::Scalar::Base overloads 'bool' by calling 'size'. Aha!

    When I change the while condition to do "defined my $arg = shift", it works correctly. Methinks a patch is in order...

    This has been an interesting course of investigation. I hope y'all have not found it boring.

    yours,
    Michael