Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Why is a hash the default "object" in oo perl?

by theAcolyte (Pilgrim)
on Jul 17, 2004 at 22:22 UTC ( #375293=perlmeditation: print w/ replies, xml ) Need Help??

 

I'm sorta newish to OO

Okay, I'll admit I've just recently (the last 9 months) been exposted to, and used Object Oriented programming. There are parts of it I like a lot, parts I find stupid or silly, and parts I've yet to understand. This meditation falls under the last category.

 

Is this an unfriendly standard?

It seems that the accepted standard in objects is to bless a hash ref. At the same time, almost everything I read about OO perl exclaims you can use whatever you want as your object. Okay fine. Here's my problem: if you, programmer Xman, write an OO module using a blessed hash ref, and I, theAcolyte, want to subclass/extend/inherit it, I'm effectively tied to using whatever you used, correct? If you have any info stored as $self->{'key'} = value my changing the object type is going to break your code.

Futhermore, if YOU change the underlying implementation of your object, MY code which inherits your object will break. Right?

Have I gotten anything wrong so far?

Next, I just today read a perlmonks post about private variables, and it suggested using a my $var at the top of the package then $obj->{'_myvar'} = \$var; to access it. Same problems I've already listed.

 

I was thinking ...

Wouldn't it make a lot more sense to have a scalar as the default object?

 

Accidental Class Variables

Okay, so you can no longer store instance variables in the object -- but you should be using accessors/mutators anyway, or so I've read. So, how do you deal with instance data? I mulled over this for a while and tried -- based on something I'd seen somewhere -- something like this:

{ my $age; sub age { my $self = shift; return $age unless @_; $age = shift; }}

The problem is, this created a *class* variable (I'm still not sure why) and every instance of the object ended up with the same data.Well, while that solves a completely different problem ... it wasn't what I needed. Eventually I wound up with this:

{ my %agedata; sub age { my $self = shift; return $age{$self} unless @_; $age{$self} = shift; }}

And it seems to work pretty well. Since a reference is a location in memory (err.. I think? Am I right?) each one should be unique ... so we can store *instance* data for each object in a *class* variable. I kinda thought it was neat.

 

Yet Another Oddity

As an aside ... I (again accidently) discovered that if you do:
{{ my $variable } sub accessor { .... }}

That the raw $variable is accessible from anywhere within the package its declared. I have NO idea why. If someone can explain I'd love it. However, anyone outsi de the package -- including a package that inherits it, can't get at $variable and must use the accessor. Not sure if theres any practical purpose for this yet.

 

And, so?

Well, I would think that this type of OO coding would make it much harder for classes inheriting you to break. Also, if you change underlying implementation it won't break things inheriting you as easily. My only wonder is ... what are the major problems with doing it this way? Are there any? That's why I'm posting here... I haven't thought of any yet, but I'm positive the monks around here can set me straight ;-) ... and even if I'm shown this is completely useless it was a fun learning experience.

- theAcolyte

Edit by castaway, swapped font tags for h4 tags

Comment on Why is a hash the default "object" in oo perl?
Select or Download Code
OT: Foreground Color without Background Color considered harmful
by jZed (Prior) on Jul 17, 2004 at 23:17 UTC
    I didn't read your post because I can't read much of it. If you set the foreground color of text in HTML, you better set the background color, otherwise some twit like me will come along with a CSS using black background and your pretty font colors will disappear.
Re: Why is a hash the default "object" in oo perl?
by blokhead (Monsignor) on Jul 17, 2004 at 23:30 UTC
    Looks like you've independently discovered Abigail's favorite type of Inside-out objects. It's the only way you can subclass without caring about the base class implementation. They are a pretty good idea.

    I use inside-out objects for my database persistence module, because it also helps with synchronization. Each object corresponds to a row in some table. So instead of using the pointer address as the key to the instance data hash, I use the primary key from the database. This is a big win, because there may be many objects instantiated that correspond to the same row in the database. When one gets updated, it's updated in the central hash and any other objects corresponding to that row can see it. If I had used blessed hashrefs as objects, it would have been a big pain to push these updates out to all the necessary hashrefs.

    What your implementation is missing, however, is a DESTROY method. When doing these inside-out objects, your %agedata hash keeps filling up. When an object is destroyed, you never clean up after yourself. For persistent environments like mod_perl, this can lead to quite a memory leak in the instance data hashes. So be sure to add:

    sub DESTROY { delete $agedata{+shift}; }
    .. and clear out all instance data hashes.

    blokhead

      I use inside-out objects for my database persistence module, because it also helps with synchronization.
      I'm intrigued. Can you show us some code?

      One more thing:

      So instead of using the pointer address as the key to the instance data hash, I use the primary key from the database. This is a big win, because there may be many objects instantiated that correspond to the same row in the database.
      in contrast with:
      So be sure to add:
      sub DESTROY { delete $agedata{+shift}; }
      .. and clear out all instance data hashes.
      Surely that will pose a problem if you can have several objects pointing to the same hash item? What do you do about that, do you keep a reference count yourself?

      And I've been pondering if weak references could be used, to do the cleanup automatically for you. I just don't see how...

        I once had an LRU cache purge itself using DESTROY on linked list nodes, that knew how to delete themselves from a hash. The only way I found was to tell the nodes which lookup key they had.

        Retrospectively i would have the hash point to dually linked list nodes, which knew their keys too. Then each access would not create a new node, but rather bubble it up. That way the linked list doesn't grow.

        To purge either implementation, you just pop off the linked list, and let the ref count reach zero.

        -nuffin
        zz zZ Z Z #!perl
        See Class::Tables for the full code. The basic idea is that the object implementation is a blessed scalar holding the tuple's primary key. I can also tell what database table it belongs to by what class it's blessed into. I use these two pieces of information to address a deep hash with all the accessor data. The general idea is:
        my $DATA; sub id { ${+shift} } # $obj->column_name() # $obj->field("column_name") sub field { my ($self, $col) = @_; my $table = (ref $self)->_table; my $id = $self->id; return $DATA{$table}{$id}{$col}; }
        The DESTROY method I gave in the previous post was for the benefit of theAcolyte and the basic inside-out mechanism. You're right that in my code, since the data should stay around as long as there's an object that might need it, I have to do something a little more clever. I end up doing my own form of reference counting. Each time I make an object, I increment $COUNT{$table}{$id}. Each time an object is destroyed, I decrement that count and if the count became zero, clear out the appropriate part of the %DATA hash (and %COUNT as well).

        blokhead

      Thanks for the link to Abagail's inside-out objects. I've seen references to it before but never read up on it, and had no clue what it meant. Also, thanks for the notion about the destroy method -- you're quite right in that it didn't occour to me that when an object goes out of scope, its attributes are still existing the %agedata hash. While its not so bad in a non-persistant environment (how I ususally work) it would be bad under mod-perl.
Re: Why is a hash the default "object" in oo perl?
by dfaure (Chaplain) on Jul 17, 2004 at 23:34 UTC

    AFAIK, perl OO consists essentially in 2 (or 3) basic intrinsics and a bunch of usage rules about data structures. So, according to the perl TIMTOWTDI's way of life, you're allowed to do what you want...

    Then the following question is: How may I be sure I'm coding what I want?
    In this case, I'm sorry to tell that I've no simple answer here: In fact it really depends on what you want.

    Practically, the hash data structure is the most genuine to be used as an object (as far as you consider an object as a bag of named properties [aka key/values]).

    When you're defining an object, you're implicitly defining a contract which must be followed by everyone whishes derive from your object. This rule defines the interface of your object. Talking about Perl, as object orientation is build as an addon to the language, when devivating you're tied to parent data structure. But talking about objects, heritage is not the only possible relation between objects (quiz: what are the other one?).

    Please refer to Perl Design Patterns for more details about perl objects ans the way to associate them.

    ____
    HTH, Dominique
    My two favorites:
    If the only tool you have is a hammer, you will see every problem as a nail. --Abraham Maslow
    Bien faire, et le faire savoir...

Re: Why is a hash the default "object" in oo perl?
by revdiablo (Prior) on Jul 18, 2004 at 00:25 UTC

    Like blockhead, your post immediately made me think of inside-out objects. And I must say, the fact that you came up with the idea all on your own is quite impressive to me. It took me a while to even understand the technique when I read about it, much less did I think of it myself. Very clever! :)

      Heh ... thanks! Really what was bothering me was a statement I've read over and over in texts that deal with Object-Oriented programming (but not about using it in any particular language) which was:
      A subclass shouldn't need to know anything about its parent's implementation, and the parent shouldn't need to know anything about a child's implementation

      Or something along those lines. And while I was experimenting by trying to create a very generic "person" object (which could then become person::client, person::employee, person::vendor etc) I realized that I would end up violating both of those ideas by storing data inside a hash-ref as object

      If I had been working on this for a paid project I would have just plowed forward as I'd be the one writting the subclasses anyway ...

      However, since this was a learning experience, I was annoyed by the idea of 'cheating' and figured there had to be a way. I had once seen (here on perlmonks) using braces around a subroutine { $var; sub { ... }} to keep a variable alive between calls to the subroutine and used that in figuring out what I did.

      I think after I read/learn Abigails implementation -- which I have no doubts is much more refined then my Saturday afternoon of hacking around -- I would likely use this as my default OO style.

      theAcolyte

Re: Why is a hash the default "object" in oo perl?
by tinita (Parson) on Jul 18, 2004 at 01:51 UTC
    hmm, i thought about that and made up some code, just for fun.
    i don't know if it's really useful and correct, but it was interesting to think about, so i'm just posting this here.
    we're talking just about the object-attributes and the get/set methods, i assume.
    so, when i create a class i usually find myself defining an attribute method like:
    sub _attr { my $self = shift; my $name = shift; return $self->{"_$name"} unless @_; $self->{"_$name"} = shift; } sub name { shift->_attr(name => @_); }

    so that you can call $obj->name("new name");
    (I know i could actually use Class::MethodMaker or something similar...)

    now how could we make that more generic?
    i thought about defining a pragma 'attribute' that you can use like that:

    package Parent; use attribute sub { my $self = shift; my $name = shift; # we have an ordinary hashref for the object return \$self->{"_$name"}; }, [qw(city name)];
Re: Why is a hash the default "object" in oo perl?
by davido (Archbishop) on Jul 18, 2004 at 03:57 UTC
    As an aside ... I (again accidently) discovered that if you do:
    {{ my $variable } sub accessor { .... }}
    That the raw $variable is accessible from anywhere within the package its declared. I have NO idea why. If someone can explain I'd love it. However, anyone outside the package -- including a package that inherits it, can't get at $variable and must use the accessor. Not sure if theres any practical purpose for this yet.

    Actually, your construct above creates a lexical $variable that immediately falls out of scope. Then the sub accesses (and autovivifies) a package global named $variable, but this is not the same entity as the lexical. From that point on, your package is dealing with the package global $variable, not the lexical (which is gone, destroyed, garbage-collected, etc).

    You mention that it's not accessible outside the package (except by the accessor sub, of course). But that's not entirely true. If the package is named "mypackage", the package global $variable may be accessed from package main as: $mypackage::variable.

    Using use strict; would alert you to this issue.


    Dave

Re: Why is a hash the default "object" in oo perl?
by Zaxo (Archbishop) on Jul 18, 2004 at 04:45 UTC

    I'll just reply to the question in the title. A hash is preferred because it provides a data structure with named access. A subclass, if you can tolerate the knowledge of the superclass's implementation, can easily add hash elements to the progenitor.

    That is impure, as OO goes, but it works awfully well.

    After Compline,
    Zaxo

      Inheritance in perl is a cooperative enterprise. Modules need to be designed to allow inheritance. Modules can also be designed to disallow inheritance. I don't know if the implementation has changed since then, but some time ago I had this problem with Bit::Vector.

      I have had trouble figuring out which modules are designed to support inheritance. I end up looking at implementations to try to figure it out.

      The 'hidden implementation' idea of OO isn't a reality in many common perl modules. To me this appears to be a side effect of the TIMTOWTDIness of perl OO.

      When I am evaluating a module implementation with the idea that I might want to inherit from it, my favorite thing to find is a blessed hashref without a fancy OO-helper module. As Zaxo says, it is easy to use and works well.

      It should work perfectly the first time! - toma
        When I am evaluating a module implementation with the idea that I might want to inherit from it, my favorite thing to find is a blessed hashref without a fancy OO-helper module
        Untill you accidentally step on your parents key and break stuff that you'll never, ever track down.
Re: Why is a hash the default "object" in oo perl?
by water (Deacon) on Jul 18, 2004 at 19:29 UTC
    I've been happy using Class::MethodMaker, and using the easily defined accessors / setters for all access into the object, both from outside, from inside (the class itself), and from any subclasses. Then nobody steps on anyone else. And given all the convenience functions C:MM offers, I'm not tempted to cheat and push stuff into hash directly.

    You won't find the eight characters

    $self->{
    in my code, as I never go into $self as a hash. In fact, CMM could migrate itself to inside-out objects or blessed pumpkin bread or whatever, and I wouldn't even notice.

    The only downside I can see to this is speed, but my apps are all disk-IO-bound anyway, so the extra wrapper calls don't matter a whit.

    Your mileage may vary--

    water, water, everywhere

        Wow.

        The Class::MakeMethods docs make my head spin!

        Class::MethodMaker isn't perfect, and is pretty basic, and has gaps in the docs, but Class::MakeMethods looks like it will take a bit to understand.

        Reminds me of the first time going through Template Toolkit docs -- it looks big, it looks good, it looks overwhelming. Soon the comprehensivesness becomes friend, not foe, but whew, alot to sink in. That's the feeling I get skimming numerous numerous Class::MethodMaker docs.

        Something new to learn eventually, methinks....

        water
        not just for breakfast anymore

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlmeditation [id://375293]
Approved by Corion
Front-paged by grinder
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (8)
As of 2014-12-18 04:36 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (41 votes), past polls