Dlaczego niektórzy użytkownicy podają nazwy klas w Perlu?

12

Patrząc na Type::Tiny, widzę, że nazwa klasy w wezwaniu do Type::Tiny->newjest cytowana w oficjalnych dokumentach,

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

Dlaczego to? Czy to tylko styl? Czy są jakieś konsekwencje dla tej praktyki?

Evan Carroll
źródło

Odpowiedzi:

7

Weź prostszy przykład

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

W powyższym przykładzie stała jest Footłumaczona na „Bar”, więc "Bar"->newnie wywołuje to "Foo"->new. Jak zatrzymać rozwiązywanie podprogramu? Możesz to zacytować.

"Foo"->new();

Jeśli chodzi o wpływ na wydajność, sytuacja nie ulegnie pogorszeniu, jeśli użyje się ciągu zamiast słowa. Potwierdziłem, że generowany optree O=Deparsejest taki sam. Zasadniczo wydaje się, że lepiej jest cytować Nazwy klas, jeśli ceni się poprawność.

Jest to wspomniane w Programowaniu w Perlu (niestety w kontekście pośredniego wywołania metody )

... więc powiemy ci, że prawie zawsze możesz uciec od nagiej nazwy klasy, pod warunkiem, że dwie rzeczy są prawdziwe. Po pierwsze, nie ma podprogramu o takiej samej nazwie jak klasa. (Jeśli postępujesz zgodnie z konwencją, że nazwy podprogramów, takie jak newrozpoczynaj małe litery, i nazwy klas, takie jak ElvenRingrozpoczynaj wielkie litery , nigdy nie stanowi to problemu). Po drugie, klasa została załadowana jednym z

use ElvenRing;
require ElvenRing;

Każda z tych deklaracji zapewnia, że ​​Perl wie, że ElvenRingjest nazwą modułu, co zmusza każdą nagą nazwę, taką jak newprzed nazwą klasy, ElvenRingdo interpretacji jako wywołanie metody, nawet jeśli zdarzy się, że zadeklarujesz własny newpodprogram w bieżącym pakiecie.

I to ma sens: zamieszanie może się zdarzyć tylko wtedy, gdy twoje podprogramy (zwykle małe litery) mają takie same nazwy jak klasa (zwykle wielkie litery). Może się to zdarzyć tylko w przypadku naruszenia powyższej konwencji nazewnictwa.

tldr; prawdopodobnie dobrze jest zacytować swoje nazwy klas, jeśli je znasz i cenisz poprawność nad bałaganem.

Uwaga dodatkowa: alternatywnie możesz przerwać rozwiązywanie słowa-słowa w funkcji poprzez umieszczenie a ::na jego końcu, na przykład powyżej Foo::->new.


Dziękuję Ginnzowi na reddit za zwrócenie mi na to uwagi i Toby'emu Inksterowi za komentarz (chociaż nie miało to dla mnie sensu przy pierwszym czytaniu).

Evan Carroll
źródło
2
Lub Foo::->new, jak dowiedziałem się od ikegami.
tłum
@mob tak, książka Perla wspomina o tym (wprost), nie jestem pewien, czy powinienem to tam umieścić, czy nie? lol.
Evan Carroll
@mob Też dodałem jako przypis. Chociaż nie jestem pewien, czy mi się podoba.
Evan Carroll
1
Foo::ma tę zaletę, że ostrzega, jeśli Foonie jest istniejącym pakietem
ikegami,
1
Spróbuj :: Tiny jest lepszym przykładem niż Foo. W używanym kodzie Foo->można oczekiwać, że wiesz, że w twoim pakiecie nie ma takiego podprogramu. Ale jeśli używasz Try :: Tiny i wielu innych modułów cpan / innych zewnętrznych źródeł, kto powie, czy jeden z nich używa pakietu Try, a jeśli tak, to czy jest w nim napis Tiny?
ysth
0

Jawne cytowanie nazwy klasy zamiast używania samego słowa (które jest traktowane jako ciąg znaków) jest jednym z trzech sposobów uniknięcia dwuznaczności składniowej. Powołując klasę część dokumentacji perlobj wyjaśnia.

Ponieważ Perl pozwala na używanie słów kluczowych w nazwach pakietów i podprogramach, czasami niepoprawnie interpretuje to słowo. Na przykład, konstrukt Class->new()można interpretować albo jako 'Class'->new()lub Class()->new().W Angielski, że druga interpretacja brzmi „wywołać podprogram Class(), następnie wywołać new()jako metodę na wartości zwracanej Class().” Jeśli Class()w bieżącej przestrzeni nazw istnieje podprogram o nazwie , Perl zawsze będzie interpretował Class->new()jako drugą alternatywę: wywołanie do new()obiektu zwrócone przez wywołanie do Class().

Zobacz ten dziwny przypadek w akcji z pokazem poniżej.

#! /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;

Jego wydajność to

Powracający Bogus
Bogus :: nowy
Wpisz :: Tiny :: new
Wpisz :: Tiny :: new
Wpisz :: Tiny :: new

Pozostała część wyżej wymienionej sekcji dokumentacji pokazuje, jak zabezpieczyć się przed zaskakującym zachowaniem lub przypadkowymi błędami.

Możesz zmusić Perla do użycia pierwszej interpretacji ( tj. Jako wywołania metody klasy o nazwie "Class") na dwa sposoby. Najpierw możesz dodać a ::do nazwy klasy:

Class::->new()

Perl zawsze interpretuje to jako wywołanie metody.

Alternatywnie możesz podać nazwę klasy:

'Class'->new()

Oczywiście, jeśli nazwa klasy jest skalarna, Perl również zrobi właściwą rzecz:

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

Stosując się do Twojego pytania, wszystkie poniższe połączenia są równoważne.

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

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

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

Dołączenie ::do końca ma tę zaletę, że daje pomocne ostrzeżenie. Powiedz, że przypadkowo wpisałeś

Type::Tinny::->new;

To produkuje

Bareword „Type :: Tinny ::” odnosi się do nieistniejącej paczki na linii ./try 15.
Nie można zlokalizować metody obiektowej „nowa” za pośrednictwem pakietu „Type :: Tinny” (być może zapomniałeś załadować „Type :: Tinny”?) Na linii ./try 15.
Greg Bacon
źródło