What exactly does Perl's “bless” do?

时光总嘲笑我的痴心妄想 提交于 2019-11-26 18:52:02

问题


I understand one uses the "bless" keyword in Perl inside a class's "new" method:

sub new {
    my $self = bless { };
    return $self;
}    

But what exactly is "bless" doing to that hash reference ?


回答1:


In general, bless associates an object with a class.

package MyClass;
my $object = { };
bless $object, "MyClass";

Now when you invoke a method on $object, Perl know which package to search for the method.

If the second argument is omitted, as in your example, the current package/class is used.

For the sake of clarity, your example might be written as follows:

sub new { 
  my $class = shift; 
  my $self = { }; 
  bless $self, $class; 
} 

EDIT: See kixx's good answer for a little more detail.




回答2:


bless associates a reference with a package.

It doesn't matter what the reference is to, it can be to a hash (most common case), to an array (not so common), to a scalar (usually this indicates an inside-out object), to a regular expression, subroutine or TYPEGLOB (see the book Object Oriented Perl: A Comprehensive Guide to Concepts and Programming Techniques by Damian Conway for useful examples) or even a reference to a file or directory handle (least common case).

The effect bless-ing has is that it allows you to apply special syntax to the blessed reference.

For example, if a blessed reference is stored in $obj (associated by bless with package "Class"), then $obj->foo(@args) will call a subroutine foo and pass as first argument the reference $obj followed by the rest of the arguments (@args). The subroutine should be defined in package "Class". If there is no subroutine foo in package "Class", a list of other packages (taken form the array @ISA in the package "Class") will be searched and the first subroutine foo found will be called.




回答3:


Short version: it's marking that hash as attached to the current package namespace (so that that package provides its class implementation).




回答4:


This function tells the entity referenced by REF that it is now an object in the CLASSNAME package, or the current package if CLASSNAME is omitted. Use of the two-argument form of bless is recommended.

Example:

bless REF, CLASSNAME
bless REF

Return Value

This function returns the reference to an object blessed into CLASSNAME.

Example:

Following is the example code showing its basic usage, the object reference is created by blessing a reference to the package's class −

#!/usr/bin/perl

package Person;
sub new
{
    my $class = shift;
    my $self = {
        _firstName => shift,
        _lastName  => shift,
        _ssn       => shift,
    };
    # Print all the values just for clarification.
    print "First Name is $self->{_firstName}\n";
    print "Last Name is $self->{_lastName}\n";
    print "SSN is $self->{_ssn}\n";
    bless $self, $class;
    return $self;
}



回答5:


I'll provide an answer here since the ones here didn't quite click for me.

Perl's bless function associates any reference to all functions inside a package.

Why would we need this?

Let's begin by expressing an example in JavaScript:

(() => {
    'use strict';

    class Animal {
        constructor(args) {
            this.name = args.name;
            this.sound = args.sound;
        }
    }

    /* [WRONG] (global scope corruption)
     * var animal = Animal({
     *     'name': 'Jeff',
     *     'sound': 'bark'
     * }); 
     * console.log(animal.name + ', ' + animal.sound); // seems good
     * console.log(window.name); // my window's name is Jeff?
     */

    // new is important!
    var animal = new Animal(
        'name': 'Jeff',   
        'sound': 'bark'
    );

    console.log(animal.name + ', ' + animal.sound); // still fine.
    console.log(window.name); // undefined
})();

Now lets strip away the class construct and make do without it:

(() => {
    'use strict';

    var Animal = function(args) {
        this.name = args.name;
        this.sound = args.sound;
        return this; // implicit context hashmap
    };

    // the "new" causes the Animal to be unbound from global context, and 
    // rebinds it to an empty hash map before being constructed. The state is
    // now bound to animal, not the global scope.
    var animal = new Animal({
        'name': 'Jeff',
        'sound': 'bark'
    });
    console.log(animal.sound);    
})();

The function takes a hash table of unordered properties(since it makes no sense to have to write properties in a specific order in dynamic languages in 2016) and returns a hash table with those properties, or if you forgot to put the new keyword, it will return the whole global context(eg window in browser or global in nodejs).

Perl has no "this" nor "new" nor "class", but it can still have a function that behaves similarly. We won't have a constructor nor a prototype, but we will be able to create new animals at will and modify their individual properties.

# self contained scope 
(sub {
    my $Animal = (sub {
        return {
            'name' => $_[0]{'name'},
            'sound' => $_[0]{'sound'}
        };
    });

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound' => 'bark'
    });

    print $animal->{sound};
})->();

Now, we have a problem: What if we want the animal to perform the sounds by themselves instead of us printing what their voice is. That is, we want a function performSound that prints the animal's own sound.

One way to do this is by teaching each individual Animal how to do it's sound. This means that each Cat has its own duplicate function to performSound.

# self contained scope 
(sub {
    my $Animal = (sub {
        $name = $_[0]{'name'};
        $sound = $_[0]{'sound'};

        return {
            'name' => $name,
            'sound' => $sound,
            'performSound' => sub {
                print $sound . "\n";
            }
        };
    });

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound' => 'bark'
    });

    $animal->{'performSound'}();
})->();

This is bad because performSound is put as a completely new function object each time an animal is constructed. 10000 animals means 10000 performSounds. We want to have a single function performSound that is used by all animals that looks up their own sound and prints it.

(() => {
    'use strict';

    /* a function that creates an Animal constructor which can be used to create animals */
    var Animal = (() => {
        /* function is important, as fat arrow does not have "this" and will not be bound to Animal. */
        var InnerAnimal = function(args) {
            this.name = args.name;
            this.sound = args.sound;
        };
        /* defined once and all animals use the same single function call */
        InnerAnimal.prototype.performSound = function() {
            console.log(this.name);
        };

        return InnerAnimal;
    })();

    /* we're gonna create an animal with arguments in different order
       because we want to be edgy. */
    var animal = new Animal({
        'sound': 'bark',
        'name': 'Jeff'
    });
    animal.performSound(); // Jeff
})();

Here is where the parallel to Perl kinda stops.

JavaScript's new operator is not optional, without it, "this" inside object methods corrupts global scope:

(() => {
    // 'use strict'; // uncommenting this prevents corruption and raises an error instead.

    var Person = function() {
        this.name = "Sam";
    };
//    var wrong = Person(); // oops! we have overwritten window.name or global.main.
//    console.log(window.name); // my window's name is Sam?
    var correct = new Person; // person's name is actually stored in the person now.

})();

We want to have one function for each Animal that looks up that animal's own sound rather than hardcoding it at construction.

Blessing lets us use a package as the prototype of objects. This way, the object is aware of the "package" it is "referenced to", and in turn can have the functions in the package "reach into" the specific instances that were created from the constructor of that "package object":

package Animal;
sub new {
    my $packageRef = $_[0];
    my $name = $_[1]->{'name'};
    my $sound = $_[1]->{'sound'};

    my $this = {
        'name' => $name,
        'sound' => $sound
    };   

    bless($this, $packageRef);
    return $this;
}

# all animals use the same performSound to look up their sound.
sub performSound {
    my $this = shift;
    my $sound = $this->{'sound'};
    print $sound . "\n";
}

package main;
my $animal = Animal->new({
    'name' => 'Cat',
    'sound' => 'meow'
});
$animal->performSound();

Summary/TL;DR:

Perl has no "this", "class", nor "new". blessing an object to a package gives that object a reference to the package, and when it calls functions in the package, their arguments will be offset by 1 slot, and the first argument($_[0] or shift) will be equivalent to javascript's "this". In turn, you can somewhat simulate JavaScript's prototype model.

Unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime, as you need each "class" to have its own package, whereas in javascript, you don't need packages at all, as "new" keyword makes up an anonymous hashmap for you to use as a package at runtime to which you can add new functions and remove functions on the fly.

There are some Perl libraries creating their own ways of bridging this limitation in expressiveness, such as Moose.

Why the confusion?:

Because of packages. Our intuition tells us to bind the object to a hashmap containing its' prototype. This lets us create "packages" at runtime like JavaScript can. Perl does not have such flexibility(at least not built in, you have to invent it or get it from other modules), and in turn your runtime expressiveness is hindered. Calling it "bless" doesn't do it much favors neither.

What we want to do:

Something like this, but have binding to the prototype map recursive, and be implicitly bound to the prototype rather than having to explicitly do it.

Here is a naive attempt at it: the issue is that "call" does not know "what called it", so it may as well be a universal perl function "objectInvokeMethod(object, method)" which checks whether the object has the method, or its prototype has it, or its prototype has it, until it reaches the end and finds it or not (prototypical inheritence). Perl has nice eval magic to do it but I'll leave that for something I can try doing later.

Anyway here is the idea:

(sub {

    my $Animal = (sub {
        my $AnimalPrototype = {
            'performSound' => sub {
                return $_[0]->{'sound'};
            }
        };

        my $call = sub {
            my $this = $_[0];
            my $proc = $_[1];

            if (exists $this->{$proc}) {
                return $this->{$proc}->();
            } else {
                return $this->{prototype}->{$proc}->($this, $proc);
            }
        };

        return sub {
            my $name = $_[0]->{name};
            my $sound = $_[0]->{sound};

            my $this = { 
                'this' => $this,
                'name' => $name,
                'sound' => $sound,
                'prototype' => $AnimalPrototype,
                'call' => $call                
            };
        };
    })->();

    my $animal = $Animal->({
        'name' => 'Jeff',
        'sound'=> 'bark'
    });
    print($animal->{call}($animal, 'performSound'));
})->();

Anyway hopefully somebody will find this post useful.




回答6:


Along with a number of good answers, what specifically distinguishes a bless-ed reference is that the SV for it picks up an additional FLAGS (OBJECT) and a STASH

perl -MDevel::Peek -wE'
    package Pack  { sub func { return { a=>1 } } }; 
    package Class { sub new  { return bless { A=>10 } } }; 
    $vp  = Pack::func(); print Dump $vp;   say"---"; 
    $obj = Class->new;   print Dump $obj'

Prints, with the same (and irrelevant for this) parts suppressed

SV = IV(0x12d5530) at 0x12d5540
  REFCNT = 1
  FLAGS = (ROK)
  RV = 0x12a5a68
  SV = PVHV(0x12ab980) at 0x12a5a68
    REFCNT = 1
    FLAGS = (SHAREKEYS)
    ...
      SV = IV(0x12a5ce0) at 0x12a5cf0
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 1
---
SV = IV(0x12cb8b8) at 0x12cb8c8
  REFCNT = 1
  FLAGS = (PADMY,ROK)
  RV = 0x12c26b0
  SV = PVHV(0x12aba00) at 0x12c26b0
    REFCNT = 1
    FLAGS = (OBJECT,SHAREKEYS)
    STASH = 0x12d5300   "Class"
    ...
      SV = IV(0x12c26b8) at 0x12c26c8
      REFCNT = 1
      FLAGS = (IOK,pIOK)
      IV = 10

With that it's known that 1) it is an object 2) what package it belongs to, and this informs its use.

For example, when dereferencing on that variable is encountered ($obj->name), a sub with that name is sought in the package (or hierarchy), the object is passed as the first argument, etc.




回答7:


I Following this thought to guide the development object-oriented Perl.

Bless associate any data structure reference with a class. Given how Perl creates the inheritance structure (in a kind of tree) it is easy to take advantage of the object model to create Objects for composition.

For this association we called object, to develop always have in mind that the internal state of the object and class behaviours are separated. And you can bless/allow any data reference to use any package/class behaviours. Since the package can understand "the emotional" state of the object.




回答8:


For example, if you can be confident that any Bug object is going to be a blessed hash, you can (finally!) fill in the missing code in the Bug::print_me method:

 package Bug;
 sub print_me
 {
     my ($self) = @_;
     print "ID: $self->{id}\n";
     print "$self->{descr}\n";
     print "(Note: problem is fatal)\n" if $self->{type} eq "fatal";
 }

Now, whenever the print_me method is called via a reference to any hash that's been blessed into the Bug class, the $self variable extracts the reference that was passed as the first argument and then the print statements access the various entries of the blessed hash.



来源:https://stackoverflow.com/questions/392135/what-exactly-does-perls-bless-do

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!