Entendo que alguém use a palavra-chave "bless" no Perl dentro do método "novo" de uma classe:
sub new {
my $self = bless { };
return $self;
}
Mas o que exatamente "abençoar" está fazendo com essa referência de hash?
Entendo que alguém use a palavra-chave "bless" no Perl dentro do método "novo" de uma classe:
sub new {
my $self = bless { };
return $self;
}
Mas o que exatamente "abençoar" está fazendo com essa referência de hash?
Respostas:
Em geral, bless
associa um objeto a uma classe.
package MyClass;
my $object = { };
bless $object, "MyClass";
Agora, quando você invoca um método $object
, o Perl sabe em qual pacote procurar o método.
Se o segundo argumento for omitido, como no seu exemplo, o pacote / classe atual será usado.
Por uma questão de clareza, seu exemplo pode ser escrito da seguinte maneira:
sub new {
my $class = shift;
my $self = { };
bless $self, $class;
}
EDIT: Veja a boa resposta do kixx para um pouco mais de detalhes.
bless
associa uma referência a um pacote.
Não importa para que serve a referência, pode ser um hash (caso mais comum), uma matriz (não tão comum), um escalar (geralmente isso indica um objeto de dentro para fora ), uma expressão regular , sub-rotina ou TYPEGLOB (consulte o livro Perl orientado a objetos: um guia abrangente de conceitos e técnicas de programação de Damian Conway para obter exemplos úteis) ou até mesmo uma referência a um identificador de arquivo ou diretório (caso menos comum).
O efeito bless
-ing tem é que permite aplicar sintaxe especial à referência abençoada.
Por exemplo, se uma referência abençoada for armazenada em $obj
(associada ao bless
pacote "Class"), $obj->foo(@args)
ela chamará uma sub-rotina foo
e passará como primeiro argumento a referência $obj
seguida pelo restante dos argumentos ( @args
). A sub-rotina deve ser definida no pacote "Class". Se não houver sub-rotina foo
no pacote "Class", será pesquisada uma lista de outros pacotes (obtidos da matriz @ISA
no pacote "Class") e a primeira sub-rotina foo
encontrada será chamada.
Esta função informa à entidade referenciada pelo REF que agora é um objeto no pacote CLASSNAME ou o pacote atual se CLASSNAME for omitido. Recomenda-se o uso da forma de abençoar com dois argumentos.
Exemplo :
bless REF, CLASSNAME
bless REF
Valor de retorno
Esta função retorna a referência a um objeto abençoado em CLASSNAME.
Exemplo :
A seguir, o código de exemplo que mostra seu uso básico, a referência do objeto é criada abençoando uma referência à classe do pacote -
#!/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;
}
Fornecerei uma resposta aqui, já que as que não foram encontradas aqui.
A função bless do Perl associa qualquer referência a todas as funções dentro de um pacote.
Por que precisamos disso?
Vamos começar expressando um exemplo em 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
})();
Agora vamos remover a construção da classe e passar sem ela:
(() => {
'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);
})();
A função pega uma tabela hash de propriedades não ordenadas (já que não faz sentido escrever propriedades em uma ordem específica em idiomas dinâmicos em 2016) e retorna uma tabela hash com essas propriedades, ou se você esqueceu de colocar a nova palavra-chave, ela retornará todo o contexto global (por exemplo, janela no navegador ou global no nodejs).
Perl não tem "isso" nem "novo" nem "classe", mas ainda pode ter uma função que se comporta de maneira semelhante. Não teremos construtor nem protótipo, mas poderemos criar novos animais à vontade e modificar suas propriedades individuais.
# self contained scope
(sub {
my $Animal = (sub {
return {
'name' => $_[0]{'name'},
'sound' => $_[0]{'sound'}
};
});
my $animal = $Animal->({
'name' => 'Jeff',
'sound' => 'bark'
});
print $animal->{sound};
})->();
Agora, temos um problema: e se queremos que o animal faça os sons sozinhos, em vez de imprimirmos a voz deles. Ou seja, queremos uma função performSound que imprima o som do animal.
Uma maneira de fazer isso é ensinando cada animal individualmente como fazer o seu som. Isso significa que cada gato tem sua própria função duplicada para executar o som.
# 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'}();
})->();
Isso é ruim porque performSound é colocado como um objeto de função completamente novo toda vez que um animal é construído. 10000 animais significa 10000 performSounds. Queremos que uma única função performSound seja usada por todos os animais que procure seu próprio som e o imprima.
(() => {
'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
})();
Aqui é onde o paralelo ao Perl meio que para.
O novo operador do JavaScript não é opcional, sem ele, "this" dentro dos métodos de objeto corrompe o escopo global:
(() => {
// '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.
})();
Queremos ter uma função para cada animal que procure o som do animal em vez de codificá-lo na construção.
Bênção nos permite usar um pacote como o protótipo de objetos. Dessa forma, o objeto está ciente do "pacote" ao qual é "referenciado" e, por sua vez, pode fazer com que as funções no pacote "alcancem" as instâncias específicas que foram criadas a partir do construtor desse "objeto de pacote":
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();
Resumo / TL; DR :
Perl não tem "isso", "classe", nem "novo". abençoar um objeto para um pacote fornece a ele uma referência ao pacote e, quando ele chama funções no pacote, seus argumentos serão deslocados em 1 slot, e o primeiro argumento ($ _ [0] ou shift) será equivalente a javascript é "isso". Por sua vez, você pode simular um pouco o modelo de protótipo do JavaScript.
Infelizmente, torna impossível (no meu entender) criar "novas classes" em tempo de execução, pois você precisa que cada "classe" tenha seu próprio pacote, enquanto que em javascript, você não precisa de pacotes, como palavra-chave "nova" cria um hashmap anônimo para você usar como um pacote em tempo de execução ao qual você pode adicionar novas funções e remover funções rapidamente.
Existem algumas bibliotecas Perl que criam suas próprias maneiras de superar essa limitação na expressividade, como o Moose.
Por que a confusão? :
Por causa dos pacotes. Nossa intuição nos diz para vincular o objeto a um hashmap contendo seu 'protótipo. Isso nos permite criar "pacotes" em tempo de execução, como o JavaScript pode. O Perl não tem essa flexibilidade (pelo menos não está embutido, é necessário inventá-lo ou obtê-lo de outros módulos) e, por sua vez, sua expressividade no tempo de execução é prejudicada. Chamar isso de "abençoar" também não ajuda muito.
O que queremos fazer :
Algo parecido com isto, mas tem ligação com o mapa do protótipo recursivo e está implicitamente vinculado ao protótipo, em vez de ter que fazê-lo explicitamente.
Aqui está uma tentativa ingênua: o problema é que "call" não sabe "o que chamou", portanto pode ser também uma função perl universal "objectInvokeMethod (objeto, método)" que verifica se o objeto possui o método , ou seu protótipo possui, ou seu protótipo, até que chegue ao fim e o encontre ou não (herança prototípica). Perl tem uma boa magia de avaliação para fazer isso, mas deixarei isso para algo que eu possa tentar fazer mais tarde.
Enfim, aqui está a idéia:
(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'));
})->();
De qualquer forma, espero que alguém ache este post útil.
my $o = bless {}, $anything;
abençoará um objeto na $anything
classe. Da mesma forma, {no strict 'refs'; *{$anything . '::somesub'} = sub {my $self = shift; return $self->{count}++};
criará um método chamado 'somesub' na classe nomeada em $anything
. Tudo isso é possível em tempo de execução. "Possível", no entanto, não torna uma boa prática usar o código diário. Mas é útil na construção de sistemas de sobreposição de objetos, como Moose ou Moo.
unfortunately it makes it impossible(to my understanding) to create "new classes" at runtime
reivindicação. Eu acho que minha preocupação acabou se resumindo ao fato de ser significativamente menos intuitivo manipular / introspectar o sistema de pacotes em tempo de execução, mas até agora eu falhei em mostrar qualquer coisa que inerentemente não possa fazer. O sistema de pacotes parece suportar todas as ferramentas necessárias para adicionar / remover / inspecionar / modificar a si próprio em tempo de execução.
Juntamente com uma série de boas respostas, o que distingue especificamente uma bless
referência -ed é que, SV
para isso, ela recebe um adicional FLAGS
( OBJECT
) e umSTASH
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'
Imprime, com as mesmas peças (e irrelevantes para isso) suprimidas
SV = IV (0x12d5530) em 0x12d5540 REFCNT = 1 BANDEIRAS = (ROK) RV = 0x12a5a68 SV = PVHV (0x12ab980) em 0x12a5a68 REFCNT = 1 BANDEIRAS = (SHAREKEYS) ... SV = IV (0x12a5ce0) em 0x12a5cf0 REFCNT = 1 BANDEIRAS = (IOK, PIOK) IV = 1 --- SV = IV (0x12cb8b8) em 0x12cb8c8 REFCNT = 1 BANDEIRAS = (PADMY, ROK) RV = 0x12c26b0 SV = PVHV (0x12aba00) em 0x12c26b0 REFCNT = 1 BANDEIRAS = (OBJETO, SHAREKEYS) STASH = 0x12d5300 "Classe" ... SV = IV (0x12c26b8) em 0x12c26c8 REFCNT = 1 BANDEIRAS = (IOK, PIOK) IV = 10
Com isso, sabe-se que 1) é um objeto 2) a qual pacote pertence, e isso informa seu uso.
Por exemplo, quando a desreferenciação dessa variável é encontrada ( $obj->name
), um sub com esse nome é procurado no pacote (ou hierarquia), o objeto é passado como o primeiro argumento, etc.
I Seguindo esse pensamento para guiar o desenvolvimento Perl orientado a objetos.
Abençoe associar qualquer referência de estrutura de dados a uma classe. Dado como o Perl cria a estrutura de herança (em um tipo de árvore), é fácil tirar proveito do modelo de objeto para criar Objetos para composição.
Para essa associação que chamamos de objeto, desenvolver sempre tendo em mente que o estado interno do comportamento do objeto e da classe é separado. E você pode abençoar / permitir que qualquer referência de dados use qualquer comportamento de pacote / classe. Uma vez que o pacote pode entender "o estado emocional" do objeto.
Por exemplo, se você tiver certeza de que qualquer objeto Bug será um hash abençoado, poderá (finalmente!) Preencher o código ausente no método Bug :: print_me:
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";
}
Agora, sempre que o método print_me é chamado por meio de uma referência a qualquer hash que foi abençoado na classe Bug, a variável $ self extrai a referência que foi passada como o primeiro argumento e, em seguida, as instruções print acessam as várias entradas do hash abençoado.