NOM

namespaces - Les espaces de nom en Perl


Les espaces de nom

Au commencement, dans les années soixante, n'importe quelle partie de votre programme avait accès à n'importe quelle autre partie de votre programme. Il s'est avéré que c'était un problème. Alors les concepteurs de langages ont inventé les variables locales, lesquelles étaient seulement visibles dans une petite partie du programme. De cette façon, les programmeurs utilisant une variable x pouvaient être sûr que personne ne pouvait trifouiller son contenu dans leur dos. Ils pouvaient également être sûrs qu'en utilisant x ils ne trifouillaient pas la variable de quelqu'un d'autre par erreur.

Tous les langages de programmation ont une philosophie et, de nos jours, la plupart de ces philosophies ont à voir avec la façon dont sont manipulés les noms de variables. Quelle variable est visible depuis quelle partie du programme, que veulent dire les noms, et quand ? Voila des détails d'une importance cruciale. Ces détails vont du baroque, dans des langages comme Lisp, au vraiment très baroque dans des langages comme C++. Perl se classerait plutôt dans la catégorie rococo.

Le problème avec Perl, ce n'est pas qu'il n'a pas de système de gestion des variables clairement défini mais plutôt qu'il a deux systèmes, tous deux à l'oeuvre en même temps. Il s'agit d'un grand secret à propos des variables Perl que beaucoup de gens apprennent trop tard : Perl a deux jeux de variables complètement séparés et indépendants. L'un et hérité de Perl 4 et l'autre est nouveau. Les deux jeux de variables sont les « variables de package » et les « variables lexicales ». Ils n'ont rien à voir entre eux.

Les variables de package sont historiquement les premières alors nous commencerons par elles. Puis nous verrons quelques problèmes avec les variables de package, et comment les variables lexicales introduites avec Perl 5 viennent résoudre ces problèmes. Enfin nous verrons comment faire en sorte que Perl découvre automatiquement les endroit où il se pourrait que vous ne manipuliez pas la variable que vous croyez, ce qui permet de trouver les erreurs avant qu'elles ne deviennent des bugs.


Les variables de package

        $x = 1

Ici, x est une variable de package. Il y a deux choses importantes à savoir à propos des variables de package : Les variables de package, c'est ce que l'on obtient si on ne précise pas autre chose. Les variables de package sont toujours globales.

« Global » signifie que ces variables de package sont toujours visibles partout, dans tous les programmes. Une fois que vous avez fait $x = 1, n'importe quelle partie du programme, même un sous-programme défini dans un autre fichier, peut inspecter et modifier la valeur de $x. Il n'y a pas d'exception à cela; les variables de package sont toujours globales.

Les variables de package sont divisées en familles appelées packages. Toutes les variables de package ont un nom en deux parties. Ces deux parties sont un peu comme le nom de famille et le prénom des variables. Vous pouvez appeler le président de la république française « Jacques », si vous voulez, mais c'est en réalité une abréviation de son nom complet qui est « Jacques Chirac ». De même, $x a un nom complet qui est quelque chose comme $main::x. La partie main est l'indicateur de package, tout comme la partie « Chirac » de « Jacques Chirac ». Jacques Chirac et Jacques Lacan sont des personnes différentes, bien qu'elles se prénomment toutes les deux « Jacques ». De la même façon, $Chirac::Jacques et $Lacan::Jacques sont des variables différentes, et $main::x et $DBI::x sont des variables différentes.

Vous êtes toujours autorisé à inclure la partie du nom de la variable qui désigne le package, et si vous le faites, Perl saura exactement de quelle variable vous parlez. Mais par souci de concision, on aime généralement omettre l'indicateur de package. Que se passe-t-il dans ce cas là ?


Le package en cours

Si vous dites juste $x, Perl considère que vous voulez parler de la variable $x dans le package en cours. Qu'est-ce que le package en cours ? C'est normalement main, mais vous pouvez le changer en écrivant :

        package Mypackage;

dans votre programme. A partir de là, le package en cours est Mypackage. Le seul rôle du package en cours est d'affecter l'interprétation des variables de package que vous nommez sans préciser de nom de package. Si le package en cours est Mypackage, alors $x signifie en réalité $Mypackage::x. Si le package en cours est main, alors $x signifie en réalité $main::x.

Si vous écrivez un module, disons le module MonModule, vous mettrez sûrement une ligne comme ceci au début de votre fichier :

        package MonModule;

A partir de là, toutes les variables que vous utilisez se trouvent dans le package MonModule et vous pouvez être pratiquement sûr que ces variables n'entrent pas en conflit avec les variables du reste du programme. Cela n'a pas d'importance si vous et l'auteur de DBI utilisez une variable appelée $x, parce que l'une sera alors $MonModule::x et l'autre sera $DBI::x.

Rappelez vous que les variables de package sont toujours globales. Même si vous n'êtes pas dans le package DBI, même si vous n'en avez jamais entendu parlé, rien ne peut vous empêcher de lire et écrire $DBI::errstr. Vous n'avez rien à faire de spécial. $DBI::errstr, comme toutes les variables de package, est une variable globale et elle est disponible globalement. Tout ce que vous avez à faire c'est de l'appeler par son nom complet pour l'atteindre. Vous pourriez même dire :

        package DBI;
        $errstr = 'Ha ha Tim!';

et cela modifierait $DBI::errstr.


Détails triviaux

Il y a seulement trois autres choses à savoir à propos des variables de package (vous pouvez les ignorer pour une première lecture) :

  1. Le package au nom vide est le même que le package main. Ainsi $::x équivaut à $main::x, quel que soit x.

  2. Certaines variables sont obligatoirement dans le package main. Par exemple si vous écrivez %ENV, Perl considère que vous parlez de %main::ENV, même si le package en cours n'est pas main. Si vous voulez parler de %Fred::ENV, vous devez l'appeler explicitement, même si le package en cours est Fred. Parmi les autre noms ayant cette spécificité on trouve INC, tous les noms constitué d'un caractère de ponctuation tels que $_ et $$ ainsi que @ARGV, STDIN, STDOUT et STDERR.

  3. Les noms de packages peuvent contenir :: mais pas les noms de variables. On peut avoir une variable appelée $DBD::Oracle::x. Cela désigne la variable x dans le package DBD::Oracle; cela n'a rien à voir avec le package DBD. Isaac Newton n'a pas de rapport avec Olivia Newton-John, et Newton::Isaac n'a pas de rapport avec Newton::John::Olivia. Même s'il apparaît qu'ils commencent tous deux par Newton, l'apparence est trompeuse. Newton::John::Olivia est dans le package Newton::John, pas dans le package Newton.

C'est tout ce qu'il y a à savoir à propos des variables de package.

Les variables de package sont globales, ce qui est dangereux parce que vous ne pouvez jamais être sûr que quelqu'un n'est pas en train de les trifouiller dans votre dos. Jusqu'à Perl 4, toutes les variables était des variables de package, ce qui était embêtant. Perl 5 à apporté de nouvelles variables qui ne sont pas globales.


Les variables lexicales

L'autre jeu de variables de Perl s'appelle les variables lexicales (nous verrons pourquoi plus tard) ou les variables privées parce qu'elles sont privées. On peut aussi les appeler parfois variables my parce qu'elles sont toujours déclarées avec my. Il est tentant de les appelées « variables locales » puisque leur effet est limité à une petite partie du programme, mais ne le faites pas ! Les gens pourraient croire que vous parlez du l'opérateur local de Perl que nous verrons plus tard. Lorsque vous voulez une « variable locale » pensez à my, pas à local.

La déclaration

        my $x;

crée une nouvelle variable appelée x, qui est presque totalement inaccessible à la plupart des parties du programme (Tous ce qui se trouve en dehors du bloc où la variable a été déclarée. Ce bloc est appelée la portée de la variable. Si la variable n'est pas déclarée dans un bloc, sa portée va de l'endroit où elle a été déclarée à la fin du fichier).

On peut aussi déclarer et initialiser une variable my en écrivant :

        my $x = 119;

On peut en déclarer et initialiser plusieurs à la fois :

        my ($x, $y, $z, @args) = (5, 23, @_);

Voyons un exemple où les variables privées vont être utiles. Examinez ce sous-programme :

        sub affiche_rapport {
          @liste_employes = @_;
          foreach $employe (@liste_employes) {
            $salaire = salaire($employee);
            affiche_rapport_partiel($employe, $salaire);
          }
        }

S'il se trouve que salaire utilise aussi une variable appelée $employe, ce sera la même variable que celle utilisée dans affiche_rapport et le fonctionnement pourrait bien être perturbé. Les deux programmeurs responsables de affiche_rapport et salaire devront se consulter pour être sûr de ne pas utiliser les mêmes variables. C'est un problème. En fait, même dans un projet de taille moyenne, c'est un problème intolérable.

La solution consiste à utiliser les variables my :

        sub affiche_rapport {
          my @liste_employes = @_;
          foreach my $employe (@liste_employes) {
            my $salaire = salaire($employee);
            affiche_rapport_partiel($employe, $salaire);
          }
        }

my @liste_employes crée une nouvelle variable de liste qui est totalement inaccessible en dehors de la fonction affiche_rapport. for my $employes crée une nouvelle variable scalaire qui est totalement inaccessible en dehors de la boucle foreach, tout comme my $salaire. Vous n'avez pas à vous préoccuper de savoir si les autres fonctions du programme trifouillent ces variables, elles ne le peuvent pas. Elles ne savent pas où les trouver car les noms ont une signification différente en dehors de la portée des déclarations my. Ces « variables my » sont parfois appelées « lexicales » car leur portée dépend uniquement du texte du programme lui-même et pas des détails de son exécution comme par exemple l'ordre dans lequel les choses sont effectuées. On peut déterminer cette portée en observant le code source, sans même savoir ce qu'il fait. Lorsque vous voyez une variable, chercher une déclaration my plus haut dans le même bloc. Si vous n'en voyez pas dans le plus petit bloc, regardez dans le bloc plus large qui le contient et ainsi de suite jusqu'à ce que vous en trouviez une. Si vous n'en trouvez pas, alors la variable est une variable de package.

Les variables my ne sont pas des variables de package. Elles ne font pas partie d'un package et n'ont pas d'indicateur de package. Le package en cours n'a aucun effet sur la façon dont elles sont interprétées. Voici un exemple :

        my $x = 17;
        package A;
        $x = 12;
        package B;
        $x = 20;
        # $x vaut maintenant 20.
        # $A::x et $B::x sont toujours indéfinies

package A change le package en cours, mais comme $x fait référence à une variable lexicale, et non à une variable de package, $x=12 n'a aucun effet sur $A::x. De même, après package B, $x=20 modifie la variable lexicale et non l'une des variables de package.

À la fin du fichier, la variable lexicale $x contient 20 et les variables de package $main::x, $A::x, et $B::x sont toujours indéfinies. Si vous en aviez eu besoin, vous auriez toujours pu y accéder en utilisant leurs noms complets.

La phrase à retenir est : « Les variables de package sont des variables globales. » Pour les variables privées, il faut utiliser my.


local et my

Presque tout le monde sait qu'il y a une fonction local en rapport avec les variables locales. Qu'est-ce que c'est, et qu'est-ce que ça a à voir avec my ? La réponse est simple, mais étrange :

my crée une variable locale. local ne fait pas cela.

D'abord, voici ce que fait vraiment local $x : ça sauve la valeur actuelle de la variable de package $x dans un endroit sûr et ça la remplace par une nouvelle valeur ou par undef si aucune nouvelle valeur n'a été spécifiée. L'ancienne valeur est restaurée lorsqu'on quitte le bloc en cours. Les variables affectées sont des variables de package auxquelles on attribue une valeur locale. Mais les variables de package sont toujours globales, et une une variable de package locale ne fait pas exception. Pour voir la différence, essayer ceci :

        $lo = 'global';
        $m  = 'global';
        A();
        sub A {
          local $lo = 'AAA';
          my    $m  = 'AAA';
          B();
        }
        sub B {
          print "B ", ($lo eq 'AAA' ? 'peut' : 'ne peut pas') ,
          " voir la valeur de lo assignée par A.\n";
          print "B ", ($m  eq 'AAA' ? 'peut' : 'ne peut pas') ,
          " voir la valeur de m assignée par A.\n";
        }

Cela affiche :

        B peut voir la valeur de lo assignée par A.
        B ne peut pas voir la valeur de m assignée par A.

Que s'est-il passé ? La déclaration local dans A a sauvé une nouvelle variable temporaire, AAA, dans la variable de package $lo. L'ancienne valeur, global, sera restaurée une fois que A sera terminée, mais avant que cela se produise, A appelle B. B n'a pas de problème pour accéder au contenu de $lo puisque $lo est une variable de package et les variables de package sont toujours accessibles de partout. Ainsi elle voit la valeur AAA assignée par A.

Au contraire, my a créé une nouvelle variable, $m, de portée lexicalement limitée, qui est uniquement visible à l'intérieur de la fonction A. En dehors de A, $m garde son ancienne valeur : elle fait référence à la variable de package $m qui est toujours globale. C'est la variable que B voit. Elle ne voit pas la valeur AAA parce que la variable qui a cette valeur est une variable lexicale et existe seulement à l'intérieur de A.


A quoi sert local ?

Comme local ne crée en fait pas de variable locale, il n'est pas très utile. Si, dans l'exemple ci-dessus, B venait à modifier la valeur de $lo, alors la valeur assignée pas A serait écrasée. C'est exactement ce que nous ne voulons pas qu'il se produise. Nous voulons que chaque fonction ait ses propres variables qui soient inaccessibles par les autres. C'est ce que fait my.

Pourquoi local existe alors ? La réponse est à 90% historique. Les premières version de Perl n'avait que des variables globales. local était facile à implémenter et a été ajouté à Perl 4 comme une solution partielle au problème des variables locales. Plus tard, dans Perl 5, plus de travail a été accompli, et de vraies variables locales ont été ajoutées au langage. Mais le nom local était déjà pris, alors la nouvelle fonctionnalité a été appelée my. my a été choisie parce qu'elle suggère l'idée de privé et aussi parce que c'est très court. La petitesse du mot est supposée vous encourager à l'utiliser à la place de local. my est également plus rapide que local.


Quand utiliser my et quand utiliser local ?

Toujours utiliser my ; ne jamais utiliser local.

Facile, non ?


Autres propriétés des variables my

À chaque déclaration my, Perl crée une toute nouvelle variable. Ce code, par exemple, affiche cinquante fois x=1.

        for (1 .. 50) {
          my $x;
          $x++;
          print "x=$x\n";
        }

Vous obtenez un nouveau $x initialisé à undef à chaque tour de boucle.

Si la déclaration était à l'extérieur de la boucle, elle ne serait effectuée qu'une seule fois, nous n'aurions donc qu'une seule variable.

        { my $x;
          for (1 .. 50) {
            $x++;
            print "x=$x\n";
          }     
        }

Cela affiche x=1, x=2, x=3, ... x=50.

Vous pouvez utiliser cela pour un truc utile : dans une fonction qui a besoin de mémoriser une valeur d'un appel à l'autre. Par exemple considérons un générateur de nombres aléatoires (comme la fonction rand de Perl). Un générateur de nombres aléatoires classique possède une graine (NDT: ``seed'' en anglais). Cette graine est simplement un nombre. Quand vous demandez un nombre aléatoire, la fonction fait des opérations arithmétiques qui brouillent la graine, et elle renvoie le résultat. Elle sauve également le résultat et l'utilise comme graine lors de l'appel suivant.

Voici le code typique : (Je l'ai volé au C ANSI standard, mais il n'est pas terrible, alors ne l'utilisez pas pour quelque chose d'important).

        $seed = 1;
        sub my_rand {
          $seed = int(($seed * 1103515245 + 12345) / 65536) % 32768;
          return $seed;
        }

Et sa sortie typique :

        16838
        14666
        10953
        11665
        7451
        26316
        27974
        27550

Mais il y a un problème, $seed est une variable globale, ce qui signifie que nous devons vérifier que personne ne la modifie par inadvertance. Ou bien il pourrait y toucher en connaissance de cause, ce qui aurait des conséquences sur le reste du programme. Qu'adviendrait-il, par exemple, s'il s'agissait d'un programme de jeu et que quelqu'un vienne tripoter le générateur de nombres aléatoires ?

Mais nous ne pouvons pas déclarer $seed comme un variable my dans la fonction :

        sub my_rand {
          my $seed;
          $seed = int(($seed * 1103515245 + 12345) / 65536) % 32768;
          return $seed;
        }

En effet elle serait initialisée à undef chaque fois que nous appelons my_rand. Nous avons besoin de retenir sa valeur entre les appels de my_rand.

Voici la solution :

        {
          my $seed = 1;
          sub my_rand {
            $seed = int(($seed * 1103515245 + 12345) / 65536) % 32768;
            return $seed;
          }
        }

La déclaration est en dehors de la fonction, ainsi elle ne se produit qu'une fois, lorsque le programme est compilé, pas à chaque fois que la fonction est appelée. Mais c'est une variable my, et elle est dans un bloc, ce qui la rend accessible uniquement au code situé à l'intérieur du bloc. my_rand est la seule autre chose dans le bloc, donc la variable $seed est seulement accessible à la fonction my_rand.

$seed est parfois appelée une variable statique parce qu'elle reste la même entre les appels de fonction (et aussi parce il y a une fonctionnalité équivalente en C qui est spécifiée par le mot-clé static).


my Variable Trivia

(NDT: si vous savez traduire ce titre, faites moi signe :)

Vous ne pouvez pas déclarer une variable my si son nom est un signe de ponctuation, comme $_, @_ ou $$. Vous ne pouvez pas non plus déclarer ainsi les variables de référence arrière $1, $2, etc. Les auteurs de my ont pensé que cela prêterait à confusion.

Bien sûr, vous ne pouvez pas dire my $DBI::errstr, car se serait contradictoire, cela reviendrait à dire que la variable de package $DBI::errstr est à présent une variable lexicale. Mais vous pouvez dire local $DBI::errstr, ce qui sauvegarde la valeur actuelle de $DBI::errstr et fait en sorte qu'elle soit restituée à la fin du bloc.

Depuis Perl 5.004, on peut écrire :

        foreach my $i (@list) {

Ce qui confine le $i dans la portée de la boucle. De même,

        for (my $i=0; $i<100; $i++) {

limite la portée de $i à la boucle for.


Déclarations

Si vous écrivez une fonction et que vous voulez qu'elle ait des variables privées, vous devez déclarer les variables avec my. Que se passe-t-il si vous oubliez ?

        sub function {
          $x = 42;        # Oops, on aurait du écrire my $x = 42.
        }

Dans ce cas, votre fonction modifie la variable de package globale $x. Si vous utilisez cette variable pour autre chose, ce sera un désastre pour votre programme.

Les versions récentes de Perl on un protection optionnelle contre cela que vous pouvez activer si vous le souhaiter. Si vous mettez

        use strict 'vars';

en haut de votre programme, Perl va exiger que ces variables de package aient un indicateur de package explicite. Le $x dans $x=42 n'a pas d'indicateur, alors le programme ne va même pas se compiler. Le compilateur va abandonner et donner le message d'erreur :

        Global symbol "$x" requires explicit package name at ...

Si vous voulez que $x soit une variable my privée, vous pouvez revenir en arrière et ajouter le my. Si vous voulez vraiment utiliser la variable de package globale, vous pouvez remplacer par

        $main::x = 42;

ou un autre nom de package approprié.

Dire simplement use strict active strict vars, et plusieurs autres vérifications. Voir perldoc strict pour plus de détails.

À présent supposons que vous écrivez le module Algorithms::KnuthBendix et que vous voulez la protection de strict vars mais que vous craignez de ne pas pouvoir finir le module à cause de crampes aux doigts à force de taper $Algorithms::KnuthBendix::Error tout le temps.

Vous pouvez sauver vos doigts en disant à strict vars de faire une exception :

        package Algorithms::KnuthBendix;
        use vars '$Error';

Cela évite que la variable $Algorithms::KnuthBendix::Error cause une erreur due à strict vars si vous y faites référence par son nom court $Error.

Vous pouvez aussi désactiver strict vars pour la portée du bloc en écrivant :

        {
          no strict 'vars';
          # strict vars est inactif jusqu'à la fin du bloc
          ...
        }


Pour résumer

Les variables de package sont toujours globales. Elles ont un nom et un indicateur de package. Vous pouvez omettre les indicateurs de package, auquel cas Perl utilise le package par défaut, que vous pouvez définir par la déclaration package. Pour des variables privées, utilisez my. N'utilisez pas local, c'est obsolète.

Vous devriez éviter les variables globales parce que ça peut être dur d'être sûr que deux parties du programme n'utilisent pas les variables des autres par erreur.

Pour éviter d'utiliser des variables globales par accident, ajoutez use strict 'vars' à votre programme. Cela vérifie que toutes vos variables sont soit déclarées comme privées, soit qualifiées explicitement, soit explicitement déclarées avec use vars.


Notes

Certains se sont plaint de ma maxime « Ne jamais utiliser local. ». Mais dans 97% des cas, cette maxime est parfaitement exacte. local peut être utile, mais rarement, alors je les ai laissées de côté car l'objectif d'un tutoriel est de présenter 97% d'utilité dans 50% d'espace.

J'étais quand même embêté d'avoir beaucoup de mails rébarbatifs me disant « Tu as oublié de mentionner que local peut servir à ceci ou cela, tu sais ». Alors j'ai écrit l'article Sept usages utiles de local pour me débarrasser des gens qui me fatiguaient avec local.

Voici un autre sujet intéressant que j'ai laissé de côté par souci de clarté et pour ne pas surcharger. J'ai reçu un mail de Robert Watkins à propos d'un programme qu'il avait écrit et qui ne fonctionnait pas. Voici à quoi ressemblait le bogue :

        my $x;
        for $x (1..5) {
                  s();
        }
        sub s { print "$x, " }

Robert voulait afficher 1, 2, 3, 4, 5, mais ça ne marchait pas. À la place, cela affichait , , , , , . Où étaient donc passées les valeurs de $x ?

Le truc ici c'est que normalement, quand vous écrivez quelque chose comme ça :

        for $x (...) { }

Perl veut confiner la valeur de la variable d'index à l'intérieur de la boucle. Si $x est une variable de package, il fait comme si vous aviez écrit :

        { local $x; for $x (...) { } }

En revanche, si $x est une variable lexicale, il fait comme si vous aviez écrit :

        { my $x;    for $x (...) { } }

cela signifie que la variable d'index de boucle ne sera pas propagée aux sous-programmes, même si elle est dans la portée de la déclaration originale.

J'aurais probablement pu éviter de faire aussi long puisque la page de manuel perlsyn décrit cela très bien :

« ...la variable est implicitement locale à la boucle et reprend sa valeur précédente à la sortie de la boucle. Si la variable était précédemment déclaré par my, elle utilise cette variable au lieu de celle qui est globale, mais elle est toujours locale à la boucle. (A noter qu'une variable de portée lexicale peut poser des problèmes si vous avez un sous-programme ou une déclaration de format à l'intérieur de la boucle qui y fait référence.) »

À mon avis, donner une portée lexicale à la variable d'index était sûrement une erreur. Si vouliez ça, vous auriez écrit for my $x ... à la place. J'aurais aimé que ça localise la variable lexicale : ça aurait sauvé la valeur de la variable lexicale avant la boucle et ça l'aurait restauré après la boucle. Mais pour des raisons techniques c'est impossible parce que ça ne marche pas non plus :

        my $m;
        { local $m = 12;
          ...
        }

Le local échoue avec ce message d'erreur :

        Can't localize lexical variable $m...

Il y a eu une discussion sur P5P à propos de l'éventualité de rendre cela possible, mais ça n'a pas l'air trivial.

Ajouté le 05/01/2001 : Il y a une petite saloperie qui prends les gens par surprise. Soit le programme suivant :

        use strict 'vars';
        my @lines = <>;
        my @sorted = sort backwards @lines;
        print @sorted;
        sub backwards { $b cmp $a }

Ici nous n'avons déclaré ni $a, ni $b, ce sont donc des variables globales. En fait, il est nécessaire qu'elles soient globales parce que l'opérateur sort doit être capable de les affecter pour la fonction backwards. Pourquoi strict ne provoque-t-il pas une erreur ?

Les variables $a et $b sont épargnées par la vérification de strict vars exactement pour cette raison.


AUTEUR

Mark Jason Dominus


VERSION ORIGINALE

http://perl.plover.com/FAQs/Namespaces.html

(Attention : ce document est dynamique et choisit sa langue en fonction de celles demandées par votre navigateur.)


TADUCTION

Alex Marandon <al@alpage.org>