Por que alguns usuários citam nomes de classe no Perl?

12

Observando Type::Tiny, vejo que o nome da classe na chamada para Type::Tiny->newé citado nos documentos oficiais,

my $NUM = "Type::Tiny"->new(
   name       => "Number",
   constraint => sub { looks_like_number($_) },
   message    => sub { "$_ ain't a number" },
);

Por que é isso? Isso é mero estilo? Existe alguma ramificação de desempenho para essa prática?

Evan Carroll
fonte

Respostas:

7

Veja um exemplo mais simples

package Foo { sub new { die 7  } };
package Bar { sub new { die 42 } };
sub Foo { "Bar" }
Foo->new();

No exemplo acima, a constante Fooresolve para "Bar", portanto, isso "Bar"->newnão chama "Foo"->new. Como você impede a sub-rotina de resolver? Você pode citá-lo.

"Foo"->new();

Quanto à implicação no desempenho, as coisas não são piores usando uma string em vez de uma palavra de barra. Confirmei que o optree gerado por O=Deparseé o mesmo. Como regra geral, parece que é melhor citar os nomes de classe se você valoriza a correção.

Isso é mencionado em Program Perl, (infelizmente no contexto da invocação indireta de método )

... portanto, diremos que você quase sempre pode se dar bem com um nome de classe, desde que duas coisas sejam verdadeiras. Primeiro, não há sub-rotina com o mesmo nome que a classe. (Se você seguir a convenção de que nomes de sub-rotinas como newiniciar em minúsculas e nomes de classe como ElvenRinginiciar em maiúsculas , isso nunca será um problema). Segundo, a classe foi carregada com um dos

use ElvenRing;
require ElvenRing;

Qualquer uma dessas declarações garante que o Perl sabe que ElvenRingé um nome de módulo, o que força qualquer nome simples, como newantes do nome da classe, ElvenRinga ser interpretado como uma chamada de método, mesmo se você declarar uma newsub - rotina própria no pacote atual.

E isso faz sentido: a confusão aqui só pode acontecer se suas sub-rotinas (normalmente em minúsculas) tiverem o mesmo nome que uma classe (geralmente em maiúsculas). Isso só pode acontecer se você violar a convenção de nomenclatura acima.

tldr; provavelmente é uma boa idéia citar seus nomes de classe, se você os conhece e valoriza a correção em detrimento da confusão.

Nota lateral: como alternativa, você pode interromper a resolução de uma palavra de barra em uma função, afixando ::a no final dela, por exemplo, acima Foo::->new.


Agradeço a Ginnz no reddit por apontar isso para mim e a Toby Inkster pelo comentário (embora não tenha sentido para mim na primeira leitura).

Evan Carroll
fonte
2
Ou Foo::->new, como aprendi com o ikegami.
mob
@mob sim, o livro Perl menciona isso também (explicitamente), não tenho certeza se devo colocar isso lá ou não? ri muito.
Evan Carroll
@mob Adicionei isso também, como uma nota de rodapé. Embora eu não tenha certeza se gosto.
Evan Carroll
11
Foo::tem a vantagem de que avisa se Foonão é um pacote existente
Ikegami
11
Experimente: Tiny é um exemplo melhor que Foo. No seu código usando, Foo->pode-se esperar que você saiba que não existe essa sub-rotina no seu pacote. Mas se você estiver usando o Try :: Tiny e vários outros módulos cpan / outros de fontes externas, quem dirá se um deles está usando um pacote Try e, em caso afirmativo, se há um sub Tiny nele?
ysth 26/11/19
0

A citação explícita do nome da classe em vez de usar uma palavra de barra (que é tratada como uma string) é uma das três maneiras de evitar a ambiguidade sintática. A seção Invocando métodos de classe da documentação do perlobj explica.

Como o Perl permite que você use palavras de barras para nomes de pacotes e sub-rotinas, às vezes ele interpreta incorretamente o significado de uma palavra de barra. Por exemplo, a construção Class->new()pode ser interpretado como quer 'Class'->new()ou Class()->new().Em Inglês, que segunda interpretação lê como “chamar uma sub-rotina chamada Class(), em seguida, chamar new()como um método sobre o valor de retorno Class().” Se houver uma sub-rotina nomeada Class()no namespace atual, o Perl sempre interpretará Class->new()como a segunda alternativa: uma chamada para new()o objeto retornado por uma chamada para Class().

Veja este caso estranho em ação com a demonstração abaixo.

#! /usr/bin/env perl

use strict;
use warnings;

sub Type::Tiny { print "Returning Bogus\n" ; return "Bogus" }

sub Type::Tiny::new { print "Type::Tiny::new\n" }

sub Bogus::new { print "Bogus::new\n" }

my $class = "Type::Tiny";

Type::Tiny->new;
Type::Tiny::->new;
"Type::Tiny"->new;
$class->new;

Sua saída é

Retornando Bogus
Bogus :: novo
Tipo :: Minúsculo :: novo
Tipo :: Minúsculo :: novo
Tipo :: Minúsculo :: novo

O restante da seção de documentação acima mostra como se proteger contra comportamentos surpreendentes ou erros inadvertidos.

Você pode forçar o Perl a usar a primeira interpretação ( ou seja , como uma chamada de método na classe nomeada "Class") de duas maneiras. Primeiro, você pode anexar ::a ao nome da classe:

Class::->new()

Perl sempre interpretará isso como uma chamada de método.

Como alternativa, você pode citar o nome da classe:

'Class'->new()

Obviamente, se o nome da classe estiver em um escalar Perl também fará a coisa certa:

my $class = 'Class';
$class->new();

Aplicando-se à sua pergunta, todas as chamadas abaixo são equivalentes.

Type::Tiny::->new(  );

"Type::Tiny"->new(  );

my $class = "Type::Tiny";
$class->new(  );

Anexar ::ao final tem a vantagem de produzir um aviso útil. Digamos que você digitou acidentalmente

Type::Tinny::->new;

Isso produz

A palavra de barra "Type :: Tinny ::" refere-se ao pacote inexistente na linha ./try 15.
Não é possível localizar o método de objeto "new" através do pacote "Type :: Tinny" (talvez você tenha esquecido de carregar "Type :: Tinny"?) Na linha ./try 15.
Greg Bacon
fonte