Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Perl, what's the underlying difference between a hash and a blessed reference?

Tags:

oop

hash

perl

bless

I am new to Perl, and I would like to understand/know more about the OO parts. Say I have a "class" with only attributes; are there benefits/advantages for creating a package and blessing a hash over working on a hash directly?

for simplicity, lets consider the following example :

package Person;

sub new {
    my $class = shift;
    my $args = {Name => '', Age => 0, @_};
    
    my $self = { Name => $args->{Name},
                 Age => $args->{Age}, };
    
    bless $self, $class;
}


package main;

my $person1 = Person->new(Name => 'David', Age => 20);
my $person2 = {Name => 'David', Age => 20, Pet => 'Dog'};

print $person1->{Name} . "\n";
print $person2->{Name} . "\n";

What I would like to know is what is the difference between $person1 and $person2, beside the OO parts, beside the fact that 1 is a blessed hash and 2 is a hash reference?

Are there any benefits of working with an object, in this case, over working on a hash?

After reviewing the answers :

Thanks for all the help :)

Håkon Hægland comment has the closest answer for me, I was just wondering, consider I only have to hold, simple scalars, no special checks, no other functionality, are there benefits for a class over a simple hash (I understand that if I need extra functionality and inheritance a class will be the right tool)

like image 724
yshuki Avatar asked Mar 02 '23 13:03

yshuki


2 Answers

$person1 is a reference that has been blessed. To create objects, Perl associates referenced data with a package name. When you treat that reference as an object, Perl uses that package name to find the methods.

$person2 is a just a reference.

Since both are still just references, you can still treat each as references. That's why this still works:

print $person1->{Name} . "\n";
print $person2->{Name} . "\n";

Which one you use depends on what you are doing with it. Tools don't have utility outside of a context.

You can read entire books (many of them) about object-oriented ideas. With the plain hash, you ask for a key, you get a value. With an object-oriented interface, you use methods to ask the object to do something for you. That might return a value, or do anything else that you like. You don't have to know how the object does its work.

That interface can be stable while the underlying data or structure can change. With the plain hash, your program has to be aware of changes to the hash structure.

In simple examples, like the one you present, you don't see much benefit because you map the interface directly to the structure. If you look at more complicated examples (such as complex Perl modules), you'll see that the things you can do an access don't map directly onto the data structure that they use.

For example, supposed some part of your task segregates tasks based on how old someone is (such as various laws or regulations about storing people's data). Perhaps you have a method is_underage:

if( $person2->is_underage ) { ... }

You don't necessarily store that as an hard answer, especially since that age can vary by jurisdictions and activity. But, somehow the module figures it out. That's the point of the interface: to hide complexity.

As an example, see How a script becomes a module. In an extended example, I start with something simple, but as the problem gets a bit more complex, I start moving things around and end up with a module. But, part of that story is recognizing when an object might make things simpler.

This is something that you figure out with experience. Try it both ways. Look for clues that you are working too hard in either way. For example, some people have already recommended Moo, but consider if you need to pull in modules to do something simple. Likewise, if you are making a big system, a framework might be appropriate.

There are plenty of people out there arguing for complete OO adherence, but there's an equally (wrong :) camp arguing for bare data structures all the time. Functional and OO styles don't have to be enemies though, especially in Perl.

like image 72
brian d foy Avatar answered May 06 '23 06:05

brian d foy


If you are not going to use method calls, there is not so big difference. One thing you can do with $person1 is introspect the name of the class it was blessed into by calling ref $person1. You could also arrange for Person to inherit attributes from a base class.

Another thing you could do with Person is to provide access validation to its data attributes. So instead of $person1->{Name} you would implement a method name() that returns $person1->{Name} and perhaps does some other useful things like logging, or checking if the name is defined and so on.

For example:

#! /usr/bin/env perl

package LivingBeing;
use strict;
use warnings;
sub new {
    die "LivingBeing: Wrong number of arguments to the constructor!" if @_ != 2;
    my ( $class, $type ) = @_;

    return bless {type => $type}, $class;
}

package Person;
use strict;
use warnings;
use parent -norequire => qw(LivingBeing);
sub new {
    my ($class, %args) = @_;
    my $self = $class->SUPER::new('human');
    $self->{age} = 0;  # Default value
    $self->{$_} = $args{$_} for keys %args;  # Maybe override default values..
    return $self;
}

sub name {
    my $self = shift;
    my $name = $self->{name};
    warn "Undefined name attribute" if !defined $name;
    return $name;
}

package main;
use strict;
use warnings;
use feature qw(say);

my $person1 = Person->new(pet => 'cat', age => 20);
my $person2 = {name => 'David', age => 20, pet => 'dog'};

say "person1 is a : ", ref $person1;
say "person2 is a : ", ref $person2;

say "The name of person1 is: ", $person1->name;
say "The age of person1 is: ", $person1->{age};

Output:

person1 is a : Person
person2 is a : HASH
Undefined name attribute at ./p2.pl line 28.
Use of uninitialized value in say at ./p2.pl line 43.
The name of person1 is: 
The age of person1 is: 20
like image 29
Håkon Hægland Avatar answered May 06 '23 07:05

Håkon Hægland