p5-mop Inside-Out object problem

Now, p5-mop-redux is implemented by Inside-Out object.

p5-mop is not using the standard scheme where an object is simply a blessed structure (usually a HashRef). Instead, it’s using InsideOut objects, where all you get as an object is some kind of identification number (usually a simple reference), which is used internally to retrieve the object properties, only accessible from within the class. (p5-mop: a gentle introduction)

Inside-Out object hidde attribute values from out of object. Is it good?

Inside-Out object has big inconvenient feature. that is we can't see attribute values by Data::Dumper.

In hash-based object, we can see attribute values by Data::Dumper;

    use Data::Dumper;
    print Dumper $obj;

But Inside-Out object is not.

     use Data::Dumper;
     # Oh! what data contain? We can't see these data.
     print Dumper $obj;

In many cases, object has multiple hierarchy.
In this case, Let's think you dump object.

    $obj1->{obj2} = $obj2;
    $obj2->{obj3} = $obj3;
    $obj3->{obj4} = $obj4;

Perl5 design is "No restriction to programmer".
so I like hash-based object.

30 Comments

It is a work in progress, but I think for now you can do this to get information about your object:

use mop;
my $meta = mop::get_meta($myobject);

You can then use $meta->dump to give you a hashref describing your attributes and methods, or either $meta->attributes or $meta->methods to get arrayrefs of either attributes or methods. Each item is either a mop::attribute or mop::method object that you can call a dump method on, to get their values:

use mop;
class Foo { has $!x is ro = 1 }

my $foo = Foo->new;
my $meta = mop::get_meta($foo);

use Data::Dumper;
print Dumper $meta->get_attribute('$!x')->dump;

Interestingly "secure" objects - as implemented by inside-out techniques have become a commodity feature to me.
It is a bit like with strictures and warnings.

You get the "guarantee" to not inadvertently corrupt your objects by setting a value in a transparent hash structure. You get sort of a guarantee that no-one else corrupts your objects and then sends you spurious bug reports.

I am very happy to hear that the mop intends to give us introspection facilities because a lack of this feature indeed seems inconvenient.
If in any way possible I would of course favour to be able and dump whole objects as suggested.

say Dumper $obj;

Convenience and safety in one - every (Perl) programmer's dream, I guess. :-)

This is really just a limitation of what Data::Dumper can do. It's not really intended as an OO debugging tool, even if it's frequently used that way.

It's not especially hard to write a dumper designed for mop objects. (I've been lazy here and not accounted for cyclical data structures - that wouldn't be especially tough to add though.)

Of course, once mop is in core, it's not inconceivable that another core module (Data::Dumper) might add special hooks for it. Perhaps instead of outputting something along the lines of:

bless(..., "Person")

...it could output for mop objects something more like:

"Person"->new(...)

Dammit, it looks like someone (presumably STEVAN or DOY) beat me to it. There's already a dump method in mop::object!

use v5.16;
use mop;
use Data::Dumper;

class Person
{
has $!name is rw;
has $!age is rw;
}

my $bob = Person->new(name => 'Robert', age => 33);

print Dumper($bob->dump);

Yeah, I realized it too when I accidentally did $object->dump on my Reply terminal lol

excellent! :-)

Yuki, for the moment, we have mop::object;:dump, which Tobyink already commented about. It recursively calls the &dump method and so should serve all your debugging needs (and it only costs 6 extra characters).

I would love to support something that allowed users to dump the object directly without needing to call &dump, if that is possible you can be sure that we will support it.

Yes, but you seem to be making the assumption that because Dumper($obj) doesn't currently work for mop objects, it will never work.

Assuming mop becomes part of Perl core, that would be a very good argument for modifying Data::Dumper to give it explicit mop support, whatever internal structure mop ends up using for its attribute storage.

Here's an example of how Data::Dumper can be patched with mop-specific support.

The implementation of mop::anoint (envisaged to be a mop-specific equivalent of bless, which doesn't bypass constructors, etc) is left as an exercise for the reader.

I personally prefer hash-based objects myself, mostly because I am familiar with it and the concept is simple.

I don't know about the "security" aspect, but hash-based object is viewed as less _safe_ because typos like $obj->{referer} (instead of $obj->{referrer}) are not caught at compile-time. People do make typos, sometimes often.

Perhaps Stevan Little et al want to make the object system in Perl be more like Ruby instead of like Perl 5 (and Python). Some can argue that Ruby is "purer" in OO aspect than Perl 5 and Python.

Steven - Actually I am going very strongly for Perl 6 and not Ruby.

Steven - And actually, the argument that Ruby is somehow more pure is usually associated with the fact that in Ruby everything is an object (String, Number, etc). This is not the direction I am trying to take things at all.

Of course, can't believe I forgot about Perl 6. I was thinking about "every attribute going through an accessor in Ruby" for the reason some people claim Ruby is more OO-pure. I personally know very little about OO and wouldn't make that claim myself.

As Toby and Stevan said (and I said so myself, in the very beginning,) it is all a work in progress. Toby's idea of Data::Dumper being able to dump mop objects just like regular hashrefs is probably the way to go, so I think it is early for you to dismiss mop just yet.

Still, I also think it is good to keep a critical eye on this, especially being a "redux", so thanks for the feedback! :)

@yuki - We are trying to keep the mop very minimal, and we are especially trying to keep the changes that MOP requires in the core minimal.

I think what you perceive as complexity is really unfamiliarity.

Is there a case where external code would want access to the internal state of an object that would not also be provided by introspection via the meta object? Pretty much the point of a MOP is to mediate this kind of access.

@chris - Yes, currently you have to do this:

my $value_in_foo = mop::get_meta('MyClass')->get_attribute('$!foo')->fetch_data_in_slot_for($my_instance);

Kind of long, but still accessible.

Hash-based objects are indeed simple, and easy to understand. However, as others pointed out, they are also easily modifiable by the users of your classes.

Even if they are easily dumpable, the most you can get is the information about the attributes. You can't really get any information about the methods provided by the class by using Data::Dumper. There is at least one other module, Data::Printer, which does display the list of public and private methods, but not more than that.

The new mop provides much better capabilities for introspecting objects, and I expect the existing dumper modules on CPAN will be suitably updated to take advantage of these and provide a richer experience.

The future with mop-in-core-Perl can only be brighter. I just hope it's sooner than later.

Yuki: It's not about the users of your class "attacking". It's for their own safety that object implementations are better to be opaque and the internals are available only via defined interfaces.

Yes, it's true that mop allows you to access the internal data. But only via it's own interface.

Not allowing the internals of an object to be accessed directly, but only via advertised interfaces, means the class implementation and the use of the class are not tightly coupled, and either can change without affecting the other.

I don't understand advantage of this. If document provide only accessor, user don't need to know internal data. Accessor work as public interface. This is not relevant that internal data is saved as hash reference or inside-out object.

So you have a class. It’s a useful class, so you put it on CPAN. Because it’s useful, many people use it. Some people write subclasses for it. Some of these subclasses store their own data in the object. One of them uses a key, say, _total, with an underscore because it didn’t want to break your class. You are unaware of this. You write a new version of the class, and for some feature you want to store a total as private information… and you don’t want to break subclasses, so you put an underscore and store it in _total. You release this new version to CPAN. Now that subclass is broken (or your class, or actually most probably both).

This is not about dangerous attackers.

It’s not because of dangerous attackers that we use lexical variables either, instead of just using global variables and local everywhere. It’s so that when we read one piece of code we can understand it without first needing to read, understand and keep in mind every single other piece of the code also.

Is it less convenient when every piece of the code is strictly isolated? Of course it is.

E.g. if everything were written using global variables and local, you could achieve some cool inheritance-like behaviours by locally temporarily overriding global values. (Much like you can do now by locally overriding subs, which are after all stored in package globals…) This can be very useful in shell programming. But that doesn’t mean I would write any large system in shell.

If the storage of instance data is automatically strictly isolated by class, you can write OO code without worrying about how not to cause unintended interactions or choosing to simply live with the risk and then very rarely paying a high price for that. In the example with your popular class on CPAN, the person who writes the subclass won’t stop to put an underscore on the attribute to avoid accidental conflicts, because accidental conflicts cannot happen. And when you write your new version of the class, you will not think about it either. You simply needn’t think about conflicts, because they cannot happen accidentally.

I don’t understand why $obj->dump is supposed to be so much less convenient than Dumper($obj) anyway (as long as there aren’t 1,500 different ways to dump things which all depend on what kind of thing it is). (And as mentioned, if p5-mop becomes core, there’s no reason Data::Dumper couldn’t support it directly, in which case the argument would seem to me to evaporate entirely. I understand even less what you think is still a problem then.)

Leave a comment

About Yuki Kimoto

user-pic I'm Perl Programmer. I LOVE Perl. I want to contribute Perl community and Perl users.