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

Hi monks, I have recently started working on code written by my predacessor in OOP. The code has quite a lot of object variables, for example:
print STDERR "$self->{Find}->{baseUrl}\n"; print STDERR "$self->{confCode} \n"; print STDERR "$self->{db}\n"; print STDERR "$self->{Find}->{userName};
This can sometimes get confusing, especially if I accidently mistype one of the variables (e.g. $self->Type instead of $self->type).
My question: Is there a way I can initialize object variables and then get warnings when one of the variables is written wrong, similar to the strict module for regular Perl variables? For example, I would get a warning for this code:
$self->{type} = "cat"; print "$self->{typpe}\n";
Thanks in advance
Guy Naamati

And if you wonder,what I am doing, as I am heading for the sink,
I am spitting out all the bitterness, Along with half of my last drink.

-- Susan Vega

Replies are listed 'Best First'.
Re: Using the strict module in object oriented programming
by davorg (Chancellor) on Jul 25, 2006 at 13:09 UTC

    Yes, this is one of the standard complaints with the usual method of creating objects in Perl. I can think of two options for you, but both of them are quite a lot of work.

    Firstly, you could stop using the hash contents directly and only access your objects' attributes with accessor methods. You can even do this for object access within the class itself.

    Secondly you can take a look at Inside-Out Objects. This is a new way of building objects in Perl. It's becoming quite popular and it specifically addresses your problem.

    Update: Changed reference to something a bit more recent. But see also the references in xdg's post below.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

      And just to throw my hat in the ring in favor of inside-out objects, I liked 'em when I first saw 'em, then I saw issues with serialization, destruction, threading, debugging, and so on. Unfortunately, I've been spoiled with a couple of years of working with really strong programmers. Then I move to a codebase with lots of crufty inherited code and programmers trying to work with this and I've completely changed my mind about inside-out objects. I strongly recommend them.

      Cheers,
      Ovid

      New address of my CGI Course.

        ... then I saw issues with serialization, destruction, threading, ...
        Object::InsideOut takes care of all these issues for you.

        Remember: There's always one more bug.
      Secondly you can take a look at Inside-Out Objects

      Ick. No offense, but that's a really outdated reference on the subject. My scratchpad has a section "Object-Oriented Perl - Inside-Out Technique" that has a more comprehensive set of Perlmonks posts on inside-out objects. For an even more comprehensive treatment, my YAPC::NA inside-out tutorial slides are online.

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        My scratchpad has a section "Object-Oriented Perl - Inside-Out Technique"
        Please do not point to your scratchpad in nodes. A scratchpad is not an official place for publication, it can change any time you like and there's nothing we can do about it, actually, it is intended to be changed at will.

        If you really feel your stuff is worth our while, for making it public in a permanent way, then publicize it somewhere more permanent. Like, here. Why point to your scratchpad, if you can just copy the relevant contents?

      I would highly recommend using Inside Out objects. They keep others from dipping into the internals of your objects and thereby tying your hands through their poor practices.

      Inside Out object do have limitations of sometimes being inconvenient for you as a user, but you as the maintainer can minimize those inconveniences by providing methods to dump the object, etc.

      Perhaps more importantly for large systems, though, is the fact that method accessors can impose a performance hit by requiring the method call (as opposed to the hash look-up). So make sure that your accessors are well optimized in these situations.

      If you have massive amounts of code that treat your objects as hashes and you don't want to refactor all of that code, you could look at refactoring your object to actually have a hash-based interface, but do method calls internallys. Lookups to non-existant "hash keys" could throw an exception. Take a look at the internals Regexp::Common (if you can follow it). It basically provides a nested hash interface, but each level of nesting acts both as a string (returns a pattern) and as a hash ref for deeper nesting. Rather cool. This could let you provide a strict "hash-like" interface to your objects.

      Ivan Heffner
      Sr. Software Engineer, DAS Lead
      WhitePages.com, Inc.
Re: Using the strict module in object oriented programming
by jhourcle (Prior) on Jul 25, 2006 at 13:07 UTC

    It sounds like you want Tie::Hash::FixedKeys

    Update: The above answers the problems that you've described in the example code -- not the title of your question, which are actually two different things:

    # accessing an item in a hashref: $hash->{'key'}; # calling an object method $object->method;

    Although an object may be a hashref, and the methods may be accessors to the items within the hash, they don't have to be the same thing, and frequently aren't.

      Admittedly, I haven't looked at this module (or really tied things in general recently), but isn't there always an additional speed hit for using a tied variable? AFAIK, this hasn't been optimized away, but, as I said, I haven't kept up on the subject.

      Due to the potential for "big" speed hits, I'd look at something like this as merely a stopgap to keep you from shooting yourself in the foot until you've moved over to an accessor/mutator based approach (dealer's choice as to how). Yes, yes, there's also a performance hit from using accessors vs direct hash keys, but it's usually going to be less than the hit you'd get from using a tied hashref.

      That way you could drop in the tying widget to stop the bleeding now, but you slow down your system slightly. Next step is to get rid of the calls to the keys and replace 'em with accessors. Step 3 is to pull the tied hash back out and get a raise for optimizing the code, conveniently leaving out the part that you slowed it down to begin with. ;-)

        Admittedly, I haven't looked at this module (or really tied things in general recently), but isn't there always an additional speed hit for using a tied variable?

        Yes. And 'use strict' and 'use warnings' also impose penalties -- you have to look at the situation, and see if the penalties from using something are worth its benefits.

        Sometimes, it's worth using something in development, and then take it out before it goes into production. Eg, in the examples from the original poster, only constants were used to access the hash, so FixedKeys isn't needed after the program's been written and tested. (if you were using potentially tainted variables as hash keys, you might want to leave it in, but you'd have to look at the costs, benefits and other risk factors for that particular case).

          A reply falls below the community's threshold of quality. You may see it by logging in.
Re: Using the strict module in object oriented programming
by imp (Priest) on Jul 25, 2006 at 13:15 UTC
    It is possible to do this using Hash::Util, which provides lock_keys.

    This does not provide security, as someone can always use unlock_keys - but it could prevent you from entering the wrong fieldname by accident.

    Personally I prefer to encapsulate such access control in accessors and mutators for the object, instead of encouraging the user to access the variables directly.
    For example, $self->set_type('cat') or $self->set('type','cat')

      Mod parent up! Oh, wait, I just did that.

      Anyway, most likely all you need to do is a use Hash::Util qw(lock_keys); and then at the end of each of your new subs do a lock_keys(%{$self}); return $self;

      Hash::Util is a standard module for perl 5.8 and up, so you don't need to do any installation, and there's no particular performance penalty for doing this either. This is exactly what you're looking for, a way to strictify the fields of a hash and catch spelling errors.

      Everyone else here is telling you interesting and useful things (in some combination of the two), but don't get distracted by them, because this particular "way to do it" is most likely the one you're after (Damien Conway did the perl world a great disservice by dissing "lock_keys", if you ask me).

      Note: The actual requirement for "lock_keys" is that it must be done after the hash fields have been defined and after the hashref has been blessed. If, for example, you don't have access to the code that generates the objects, you can do a "lock_keys" later, inside of each method that you're working with. If you go for a local "lock_keys" approach, I strongly suggest doing an "unlock_keys" at the end of the method, particularly if you're returning "$self" to allow chained method calls. Some of the existing code may expect to be able to create new keys on the fly.

        Damian's book (Perl Best Practices) is full of useful content. Some of it takes getting used to of course (e.g. always using the 'x' modifier for regex).

        I agree with you that restricted hashes are not inherently bad - they just aren't reliable for security.

        In section 15.4 he does say "Don't use restricted hashes", but he also spends several paragraphs talking about their benefits.

        Thanks, that sounds like good advice, and I think I will use it.
Re: Using the strict module in object oriented programming
by davidrw (Prior) on Jul 25, 2006 at 13:10 UTC
    It will take refactoring, but should be worth it in the long run, to use something like Class::Accessor or similar (see the "SEE ALSO" section).. That will provide you with methods for each of your attributes so that you don't have to (and be sure to find/replace everything) use the typo-suspectible hash keys.
    package Foo; use base qw(Class::Accessor); __PACKAGE__->mk_accessors(qw( Find confCode db Find type )); sub demo { my $self = shift; print STDERR join "\n", $self->Find->{baseUrl}, $self->confCode, $se +lf->db, $self->Find->{userName}; printf "type = %s\n", $self->type; }
Re: Using the strict module in object oriented programming
by ikegami (Patriarch) on Jul 25, 2006 at 14:52 UTC
    I use array objects with constants for the indexes, so I don't have that problem.
    use constant FIRST_IDX => __PACKAGE__->SUPER::NEXT_IDX(); use constant IDX_NAME => FIRST_IDX + 0; use constant IDX_ATTS => FIRST_IDX + 1; use constant NEXT_IDX => FIRST_IDX + 2; sub name { my ($self) = @_; return $self->[IDX_NAME]; }

    Something similar could be used for hashes.

    use constant IDX_NAME => 'NAME'; use constant IDX_ATTS => 'ATTS'; sub name { my ($self) = @_; return $self->{IDX_NAME()}; }

    As long as you always put the (), errors will always be caught (at run-time). A typo would lead to an error message like Undefined subroutine &main::IDX_NAMES called

Re: Using the strict module in object oriented programming
by phaylon (Curate) on Jul 25, 2006 at 15:53 UTC
    I've been ridden by the Moose lately, and I really, really love it. While I am still amazed of the power and flexibility of Perl's own OO system, I'm at least as amazed of what Moose got out of it.

    Ordinary morality is for ordinary people. -- Aleister Crowley
      While I am still amazed of the power and flexibility of Perl's own OO system, I'm at least as amazed of what Moose got out of it.

      Sometimes there is too much flexibility.

      Moose is built on Class::MOP, which implements a meta-object protocol. The MOP lays out a well-defined abstraction for the components of an object system and how they are supposed to behave and interact. That well-defined abstraction is what the Perl object system lacks, so Class::MOP builds one on top of the Perl object system and has it provide the API for things like Moose.

      Many kudos for stvn for his work on it. (And more kudos for his explaining the MOP to me at YAPC in simple words I could understand.)

      For more on MOP:

      -xdg

      Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

        Sometimes there is too much flexibility.

        Moose is built on Class::MOP, which implements a meta-object protocol. The MOP lays out a well-defined abstraction for the components of an object system and how they are supposed to behave and interact. That well-defined abstraction is what the Perl object system lacks, so Class::MOP builds one on top of the Perl object system and has it provide the API for things like Moose.
        I respectfully but hardly disagree. Without the flexibility, we'd have one solution. Maybe it would be like Moose, maybe it would be another. But it certainly couldn't evolve in the way it does. So, for my part: Hooray for flexibility.

        YMMV.

        Ordinary morality is for ordinary people. -- Aleister Crowley
Re: Using the strict module in object oriented programming
by pajout (Curate) on Jul 26, 2006 at 10:08 UTC
    I see following solution: In constructor, bless tied hash (man perltie). You will keep a chance to warn or die when unexpected key will occur (in your implementation of EXISTS, FIRSTKEY, NEXTKEY), all without change of code which uses the instances of this object.
Re: Using the strict module in object oriented programming
by Moron (Curate) on Jul 26, 2006 at 15:39 UTC
    I usually avoid embedding such things in strings, for precisely the reasons you give, although where I put them would then become a matter of convenience, so my version of your code would look something like:
    use strict; use warnings; #... print STDERR join( "\n", $self->{Find}->{baseUrl}, $self->{confCode}, $self->{db}, $self->{Find}->{userName} ) . "\n"; # assuming you wanted a trailing "\n" too

    -M

    Free your mind