Pourquoi certains utilisateurs citent-ils des noms de classe en Perl?

12

En regardant Type::Tiny, je vois que le nom de la classe dans l'appel à Type::Tiny->newest cité dans les documents officiels,

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

Pourquoi est-ce? Est-ce un simple style? Y a-t-il des ramifications de performances pour cette pratique?

Evan Carroll
la source

Réponses:

7

Prenons un exemple plus simple

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

Dans l'exemple ci-dessus, la constante se Foorésout en "Bar", donc cela n'appelle "Bar"->newpas "Foo"->new. Comment empêchez-vous la résolution du sous-programme? Vous pouvez le citer.

"Foo"->new();

Quant à l'implication des performances, les choses ne sont pas aggravées en utilisant une chaîne plutôt qu'un mot simple. J'ai confirmé que l'optree généré par O=Deparseest le même. Donc, en règle générale, il semble préférable de citer les noms de classe si vous appréciez l'exactitude.

Ceci est mentionné dans Programming Perl, (malheureusement dans le contexte de l' invocation de méthode indirecte )

... nous allons donc vous dire que vous pouvez presque toujours vous en sortir avec un nom de classe nu, à condition que deux choses soient vraies. Tout d'abord, il n'y a pas de sous-programme du même nom que la classe. (Si vous suivez la convention selon laquelle les noms de sousnewElvenRing - programmes comme start minuscule et les noms de classe comme start majuscule , ce n'est jamais un problème). Deuxièmement, la classe a été chargée avec l'un des

use ElvenRing;
require ElvenRing;

L'une ou l'autre de ces déclarations garantit que Perl sait qu'il ElvenRings'agit d'un nom de module, ce qui force tout nom nu comme newavant le nom de classe ElvenRingà être interprété comme un appel de méthode, même si vous avez déclaré un newsous - programme de votre choix dans le package actuel.

Et cela a du sens: la confusion ici ne peut se produire que si vos sous-programmes (généralement en minuscules) ont le même nom qu'une classe (généralement en majuscules). Cela ne peut se produire que si vous violez la convention de dénomination ci-dessus.

tldr; c'est probablement une bonne idée de citer vos noms de classe, si vous les connaissez et que vous appréciez l'exactitude par rapport à l'encombrement.

Remarque: vous pouvez également arrêter la résolution d'un mot nu à une fonction en apposant un ::à la fin de celle-ci, par exemple dans ce qui précède Foo::->new.


Merci à Ginnz sur reddit de me l'avoir signalé , et à Toby Inkster pour le commentaire (même si cela n'avait pas de sens pour moi à la première lecture).

Evan Carroll
la source
2
Ou Foo::->new, comme je l'ai appris d'ikegami.
mob
@mob yea, le livre Perl le mentionne aussi (explicitement), vous ne savez pas si je dois le mettre là ou non? lol.
Evan Carroll
@mob J'ai ajouté cela aussi, comme note de bas de page. Même si je ne suis pas sûr de l'aimer.
Evan Carroll
1
Foo::a l'avantage qu'il avertit si ce Foon'est pas un paquet existant
ikegami
1
Try :: Tiny est un meilleur exemple que Foo. Dans votre code, Foo->vous pouvez vous attendre à savoir qu'il n'y a pas un tel sous-programme dans votre package. Mais si vous utilisez Try :: Tiny et un certain nombre d'autres modules cpan / autres sources externes, qui peut dire si l'un d'eux utilise un package Try, et si oui, s'il contient un sous-marin Tiny?
19h
0

La citation explicite du nom de classe plutôt que l'utilisation d'un mot nu (qui est traité comme une chaîne) est l'une des trois façons d'éviter l'ambiguïté syntaxique. La section Invoking Class Methods de la documentation de perlobj explique.

Parce que Perl vous permet d'utiliser des mots nus pour les noms de packages et les noms de sous-programmes, il interprète parfois incorrectement la signification d'un mot nu. Par exemple, la construction Class->new()peut être interprétée comme 'Class'->new()ou Class()->new(). En anglais, cette deuxième interprétation se lit comme «appeler un sous-programme nommé Class(), puis appeler new()comme méthode sur la valeur de retour de Class()». S'il y a un sous-programme nommé Class()dans l'espace de noms courant, Perl interprétera toujours Class->new()comme la deuxième alternative: un appel à new()sur l'objet retourné par un appel à Class().

Voir ce cas étrange en action avec la démo ci-dessous.

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

Sa sortie est

Retour de Bogus
Bogus :: nouveau
Type :: Minuscule :: nouveau
Type :: Minuscule :: nouveau
Type :: Minuscule :: nouveau

Le reste de la section de documentation susmentionnée montre comment se protéger contre un comportement surprenant ou des erreurs involontaires.

Vous pouvez forcer Perl à utiliser la première interprétation ( c'est-à - dire comme appel de méthode sur la classe nommée "Class") de deux manières. Tout d'abord, vous pouvez ajouter un ::au nom de la classe:

Class::->new()

Perl interprétera toujours cela comme un appel de méthode.

Alternativement, vous pouvez citer le nom de la classe:

'Class'->new()

Bien sûr, si le nom de la classe est dans un scalaire Perl fera aussi la bonne chose:

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

S'appliquant à votre question, tous les appels ci-dessous sont équivalents.

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

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

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

L'ajout ::à la fin a l'avantage de produire un avertissement utile. Dites que vous avez accidentellement tapé

Type::Tinny::->new;

Cela produit

Le mot nu "Type :: Tinny ::" fait référence à un package inexistant sur la ligne 15 de ./try.
Impossible de localiser la méthode d'objet "nouveau" via le package "Type :: Tinny" (peut-être avez-vous oublié de charger "Type :: Tinny"?) À la ligne ./try 15.
Greg Bacon
la source