Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl: How to create objects on the fly?

My goal is to be able to use $obj like this:

print $obj->hello() . $obj->{foo};

And I would like to create an object inline, maybe using something like this:

my $obj = (
    foo => 1,
    hello => sub { return 'world' }
);

but when I try to use $obj as an object, I get an error saying that $obj has not been blessed. Is there some base class (like stdClass in PHP) I can use to bless the hash so that I can use it as an object?


For those that know JavaScript, I am trying to do the following, but in Perl:

# JS CODE BELOW
var obj = { foo: 1, hello: function () { return 'world' } };
echo obj.hello() + obj.foo;
like image 986
Tom Avatar asked Aug 04 '10 13:08

Tom


People also ask

Does Perl have objects?

An object within Perl is merely a reference to a data type that knows what class it belongs to. The object is stored as a reference in a scalar variable. Because a scalar only contains a reference to the object, the same scalar can hold different objects in different classes.

Is Perl Object Oriented?

Perl is an Objected Oriented, dynamic and interpreter based programming language. In object-oriented programming, we have three main aspects, which are, object, class, and methods. An object is a data type which can be specifically called as an instance of the class to which it belongs.

What is the correct syntax for defining a class in Perl?

Perl does not provide any special syntax for class definitions. A package is simply a namespace containing variables and subroutines. The only difference is that in a class, the subroutines may expect a reference to an object or the name of a class as the first argument.


2 Answers

A more Perlish approach is to create a separate namespace for your object's desired methods and to bless the object to make those methods available for your object. The code to do this can still be quite succint.

my $obj = bless { foo => 1 }, "bar";
sub bar::hello { return 'world' };

As gbacon suggests, if you're willing to write $obj->{hello}->() instead of $obj->hello(), you can skip the bless operation.

my $obj = { foo => 1, hello => sub { return 'world' } };
like image 72
mob Avatar answered Oct 31 '22 16:10

mob


Perl would require a little help to do this. Because it doesn't consider code references stored in hashes as "methods". Methods are implemented as entries into a package symbol table. Perl is more class-oriented than JavaScript, which proudly proclaims that it is more object-oriented (on individual objects).

In order to do that functionality, you would have to create a class that mapped references in this way. The way to get around methods in the symbol table is the AUTOLOAD method. If a package contains an AUTOLOAD subroutine, when a call is made to a blessed object that Perl cannot find in the inheritance chain, it will call AUTOLOAD and set the package-scoped (our) variable $AUTOLOAD will contain the full name of the function.

We get the name of the method called, by getting the last node (after the last '::') of the fully-qualified sub name. We look to see if there is a coderef at that location, and if there is, we can return it.

package AutoObject;

use strict;
use warnings;
use Carp;
use Params::Util qw<_CODE>;
our $AUTOLOAD;

sub AUTOLOAD {
    my $method_name = substr( $AUTOLOAD, index( $AUTOLOAD, '::' ) + 2 );
    my ( $self )    = @_;
    my $meth        = _CODE( $self->{$method_name} );
    unless ( $meth ) { 
        Carp::croak( "object does not support method='$method_name'!" );
    }
    goto &$meth;
}


1;

Then you would bless the object into that class:

package main;

my $obj 
    = bless { foo => 1
      , hello => sub { return 'world' }
      }, 'AutoObject';

print $obj->hello();

Normally, in an AUTOLOAD sub I "cement" behavior. That is, I create entries into the package symbol table to avoid AUTOLOAD the next time. But that's usually for a reasonably defined class behavior.

I also designed a QuickClass which creates a package for each object declared, but that contains a lot of symbol table wrangling that now days is probably better done with Class::MOP.


Given the suggestion by Eric Strom, you could add the following code into the AutoObject package. The import sub would be called anytime somebody use-d AutoObject (with the parameter 'object').

# Definition:
sub object ($) { return bless $_[0], __PACKAGE__; };

sub import { # gets called when Perl reads 'use AutoObject;'
    shift; # my name
    return unless $_[0] eq 'object'; # object is it's only export
    use Symbol;
    *{ Symbol::qualify_to_reference( 'object', scalar caller()) }
        = \&object
        ;
}

And then, when you wanted to create an "object literal", you could just do:

use AutoObject qw<object>;

And the expression would be:

object { foo => 1, hello => sub { return 'world' } };

You could even do:

object { name  => 'World'
       , hello => sub { return "Hello, $_[0]->{name}"; } 
       }->hello()
       ;

And you have an "object literal" expression. Perhaps the module would be better called Object::Literal.

like image 14
Axeman Avatar answered Oct 31 '22 17:10

Axeman