Cosa Evitare

Perl 5 non è perfetto. Alcune funzionalità sono difficili da usare correttamente. Altre non hanno mai funzionato bene. Alcune sono bizzarre combinazioni di altre funzionalità con strani casi speciali. Anche se la cosa migliore che potete fare è semplicemente di evitarle, conoscerne il motivo vi aiuterà a trovare soluzioni più efficaci.

Bareword

Perl è un linguaggio malleabile. Potete scrivere programmi nei modi più creativi, manutenibili, criptici e bizzarri che preferite. I buoni programmatori si preoccupano della manutenibilità, ma Perl non ha la presunzione di sapere che cos'è la manutenibilità secondo il vostro punto di vista.

Il parser Perl riconosce le istruzioni native e gli operatori. Usa i sigilli per identificare le variabili e altri simboli di punteggiatura per riconoscere chiamate a funzioni e metodi. Tuttavia, qualche volta il parser deve cercare di indovinare le vostre intenzioni, specialmente quando usate una bareword—un identificatore senza sigilli o altra punteggiatura sintatticamente significativa.

Usi Appropriati delle Bareword

Sebbene la direttiva strict (Direttive) sia rigida nel proibire l'uso di bareword ambigue, accetta invece alcuni usi delle bareword.

Bareword come chiavi di hash

Le chiavi di hash in Perl 5 sono generalmente non ambigue poiché il parser le può identificare come stringhe; in $giochi{flipper}, flipper è ovviamente una stringa.

Occasionalmente, questa interpretazione non è ciò che desiderate, specialmente quando intendete valutare una funzione nativa o definita da voi per produrre la chiave di hash. In tal caso, potete disambiguare fornendo degli argomenti, usando le parentesi degli argomenti di funzione oppure anteponendo un operatore più unario per forzare la valutazione della funzione nativa:

    # la chiave e` il letterale 'shift'
    my $valore = $elementi{shift};

    # la chiave e` il valore prodotto da shift
    my $valore = $elementi{shift @_}

    # il + unario usa la funzione nativa shift
    my $valore = $elementi{+shift};

Bareword come nomi di package

Anche i nomi di package in Perl 5 sono delle bareword. Se vi attenete alla convenzione per cui i nomi di package hanno iniziali maiuscole e le funzioni no, vi imbatterete raramente in collisioni di nomi, ma il parser di Perl 5 deve decidere come interpretare Package->metodo(). Significa "chiama una funzione di nome Package() e poi chiama metodo() sul suo valore di ritorno"? oppure significa "Chiama un metodo di nome metodo() nel namespace Package"? La risposta cambia a seconda del codice che il parser ha già incontrato nel namespace corrente.

Potete forzare il parser a trattare Package come un nome di package appendendo il separatore di package (::) Anche tra le persone che capiscono perché questo funziona, pochissime lo usano.:

    # probabilmente e` un metodo di classe
    Package->metodo();

    # sicuramente e` un metodo di classe
    Package::->metodo();

Bareword come nomi di blocchi di codice

I blocchi di codice con i nomi speciali AUTOLOAD, BEGIN, CHECK, DESTROY, END, INIT e UNITCHECK sono bareword che dichiarano delle funzioni senza usare l'istruzione nativa sub. Li avete già incontrati in questo libro (Generazione di Codice):

    package Scimmia::Maggiordomo;

    BEGIN { inizializza_scimmie( __PACKAGE__ ) }

    sub AUTOLOAD { ... }

Anche se potete omettere sub nelle dichiarazioni di AUTOLOAD(), poche persone lo fanno.

Bareword come costanti

Le costanti dichiarate con la direttiva constant si usano come bareword:

    # non usatelo per una vera autenticazione
    use constant NAME     => 'Bob';
    use constant PASSWORD => '|38pesce!lesso74|';

    return unless $nome eq NAME && $pass eq PASSWORD;

Notate che queste costanti non vengono interpolate in una stringa con doppia quotatura.

Le costanti sono un caso speciale di funzioni prototipate (Prototipi). Quando predichiarate una funzione con un prototipo, il parser sa come deve trattare tale funzione e vi avverte in caso ci siano ambiguità di interpretazione. Tutti i problemi dei prototipi si applicano anche alle costanti.

Usi Inappropriati delle Bareword

Indipendentemente da quanta cura mettete nel programmare, le bareword producono codice ambiguo. Nella maggior parte dei casi potete evitare di usarle, ma incontrerete svariati tipi di bareword nel codice preesistente.

Bareword nelle chiamate di funzione

Il code scritto senza usare strict 'subs' può usare delle bareword come nomi di funzione. Aggiungere delle parentesi fa sì che il codice soddisfi le restrizioni. Usate perl -MO=Deparse,-p (vedete perldoc B::Deparse) per scoprire come Perl le interpreta, e quindi aggiungete le parentesi appropriate.

Bareword come valori di hash

Alcuni pezzi di codice poco recente non si preoccupavano di quotare i valori nelle coppie degli hash:

    # pessimo stile; da evitare
    my %genitori =
    (
        madre => Anna,
        padre => Flavio,
    );

Quando non esistono né la funzione Flavio() né la funzione Anna(), Perl interpreta queste bareword come stringhe. strict 'subs', in questo caso, genera invece un errore.

Bareword come filehandle

Prima dell'introduzione dei filehandle lessicali (Riferimenti a Filehandle), tutti gli handle di file e di directory erano delle bareword. Potete quasi sempre riscrivere questo codice senza problemi in modo che usi dei filehandle lessicali. Fanno eccezione STDIN, STDOUT e STDERR, ma fortunatamente il parser di Perl è in grado di riconoscerli.

Bareword come funzioni per sort

Infine, la funzione nativa sort può ricevere come secondo argomento il nome di una funzione da usare per l'ordinamento. Anche se raramente questo nome risulta ambiguo per il parser, esso può invece confondere le persone che leggono il codice. L'alternativa di fornire un riferimento a funzione in uno scalare è solo un poco migliore:

    # stile che usa bareword
    my @ordinato = sort confronta_lunghezze @nonordinato;

    # riferimento a funzione in scalare
    my $confronto = \&confronta_lunghezze;
    my @ordinato  = sort $confronto @nonordinato;

La seconda opzione evita l'uso di bareword, ma il codice risultante ha una linea in più. Purtropppo, il parser di Perl 5 non accetta la versione con una sola linea a causa delle speciali regole di parsing di sort; non potete usare espressioni arbitrarie (come ottenere un riferimento a una funzione con nome) dove il parser si attende un blocco oppure uno scalare.

    # non funziona
    my @ordinato = sort \&confronta_lunghezze @nonordinato;

In entrambi i casi, il modo in cui sort invoca la funzione e le passa gli argomenti può essere poco chiaro (vedete perldoc -f sort per i dettagli). Dove possibile, prendete invece in considerazione l'uso della forma a blocco di sort. Se dovete usare una delle forme con funzione, ricordate di aggiungere un commento esplicativo.

Oggetti Indiretti

Perl 5 non ha un operatore new; un costruttore è una qualunque cosa che restituisce un oggetto. Per convenzione, i costruttori sono metodi di classe con nome new(), ma potete scegliere qualunque alternativa vi piaccia. Molti vecchi tutorial sugli oggetti Perl 5 promuovevano l'uso di chiamate di costruttori in stile C++ e Java:

    my $q = new CGI; # NON USARE

... invece della più esplicita chiamata di metodo:

    my $q = CGI->new();

Queste due sintassi sono equivalenti in tutti i casi, tranne quelli in cui non lo sono.

Invocazioni Indirette con Bareword

Nella forma indiretta (e, più precisamente, nel caso dativo) del primo esempio, il verbo (il metodo) precede il nome a cui si riferisce (l'oggetto). Questo va bene nei linguaggi naturali, ma in Perl 5 introduce delle ambiguità di interpretazione.

Dato che il nome del metodo è una bareword (Bareword), il parser deve indovinare qual è l'interpretazione corretta del codice attraverso l'uso di svariate euristiche. Anche se tali euristiche sono ampiamente testate e quasi sempre corrette, nei casi in cui falliscono possono creare confusione. Peggio ancora, esse dipendono dall'ordine di compilazione del codice e dei moduli.

Le difficoltà del parsing aumentano ulteriormente quando il costruttore riceve degli argomenti. In stile indiretto si potrebbe scrivere:

    # NON USARE
    my $ogg = new Class( arg => $valore );

... facendo assomigliare il nome della classe a una chiamata di funzione. Perl 5 può disambiguare molti di questi casi, ma le sue euristiche dipendono dai nomi di package che il parser ha già incontrato, dalle bareword che ha già interpretato (e da come le ha interpretate) e dai nomi di funzione che sono già stati dichiarati nel package corrente.

Immaginate di imbattervi in una funzione prototipata (Prototipi) il cui nome va in qualche modo in conflitto con il nome di una classe o di un metodo chiamato indirettamente. Anche se è una situazione rara, farne il debug è così problematico che vale la pena evitare le invocazioni indirette.

Limitazioni Scalari della Notazione Indiretta

Un altro problema legato alla sintassi indiretta è che il parser si aspetta una singola espressione scalare come oggetto. Stampare su un filehandle memorizzato in una variabile aggregata sembra facile, ma non lo è:

    # NON FUNZIONA COME CI SI ASPETTA
    say $config->{output} 'Messaggio diagnostico divertente!';

Perl tenterà di chiamare say sull'oggetto $config.

print, close e say—che sono tutte funzioni native che operano sui filehandle—operano in modo indiretto. Questo comportamento era appropriato quando i filehandle erano globali ai package, ma i filehandle lessicali (Riferimenti a Filehandle) evidenziano i problemi della sintassi a oggetti indiretta. Per risolvere tali problemi, disambiguate la sottoespressione che produce l'invocante desiderato:

    say {$config->{output}} 'Messaggio diagnostico divertente!';

Alternative alla Notazione Indiretta

La notazione diretta di invocazione non ha questi problemi di ambiguità. Per costruire un oggetto, chiamate il metodo costruttore direttamente sul nome della classe:

    my $q   = CGI->new();
    my $ogg = Class->new( arg => $valore );

Questa sintassi contiene ancora un problema di bareword dato che, se esiste una funzione di nome CGI, Perl interpreta la bareword del nome di classe come una chiamata alla funzione, ovvero:

    sub CGI;

    # avete scritto CGI->new(), ma Perl lo ha letto
    my $q = CGI()->new();

Anche se questo problema si presenta raramente, potete disambiguare i nomi di classe appendendo il separatore di package (::) oppure contrassegnando esplicitamente i nomi di classe come letterali stringa:

    # separatore di package
    my $q = CGI::->new();

    # letterale stringa non ambiguo
    my $q = 'CGI'->new();

Tuttavia, nella pratica nessuno lo fa.

Per il caso specifico delle operazioni su filehandle, l'uso del dativo è talmente prevalente che potete usare l'approccio di invocazione indiretta se racchiudete l'invocante desiderato tra parentesi graffe. Se usate Perl 5.14 (o se caricate IO::File oppure IO::Handle), potete usare dei metodi sui filehandle lessicali Anche se quasi nessuno li usa con print e say..

Il modulo CPAN Perl::Critic::Policy::Dynamic::NoIndirect (che è un plugin di Perl::Critic) può identificare le invocazioni indirette durante le revisioni del codice. Il modulo CPAN indirect può identificare e proibire il loro uso nei programmi in esecuzione:

    # genera un warning in caso di invocazione indiretta
    no indirect;

    # solleva un'eccezione in caso di invocazione indiretta
    no indirect ':fatal';

Prototipi

Un prototipo è un insieme opzionale di metadati associato ad una funzione che cambia il modo in cui il parser interpreta i suoi argomenti. Anche se, a un'analisi superficiale, i prototipi ricordano le segnature di funzione di altri linguaggi, in realtà sono molto diversi.

I prototipi permettono agli utenti di definire delle funzioni che si comportano come quelle native. Considerate la funzione nativa push, che riceve un array e una lista. Mentre normalmente Perl 5 appiattirebbe l'array e la lista in un'unica lista da passare a push, il parser sa che non deve appiattire l'array, in modo che push possa modificarlo sul posto.

I prototipi di funzione sono parte delle dichiarazioni:

    sub qui        (&@);
    sub quo        ($$) { ... }
    my  $qua = sub (&&) { ... };

Un prototipo associato a una dichiarazione anticipata deve corrispondere al prototipo associato alla dichiarazione della funzione; in caso contrario, Perl genera un warning. Curiosamente, potete omettere il prototipo nella dichiarazione anticipata e includerlo nella dichiarazione vera e propria—ma non ci sono ragioni per farlo.

La funzione nativa prototype riceve il nome di una funzione e restituisce una stringa che rappresenta il suo prototipo. Per vedere il prototipo di una funzione nativa, usate la forma CORE:::

    $ perl -E "say prototype 'CORE::push';"
    \@@
    $ perl -E "say prototype 'CORE::keys';"
    \%
    $ perl -E "say prototype 'CORE::open';"
    *;$@

prototype restituisce undef per le funzioni native che non potete emulare:

    say prototype 'CORE::system' // 'undef'
    # undef; non potete emulare la funzione nativa system

    say prototype 'CORE::prototype' // 'undef'
    # undef; la funzione nativa prototype non ha un prototipo

Vi ricordate di push?

    $ perl -E "say prototype 'CORE::push';"
    \@@

Il carattere @ rappresenta una lista. Il backslash impone l'uso di un riferimento all'argomento corrispondente. Quindi, questo prototipo significa che push riceve un riferimento ad array e una lista di valori. Potreste scrivere la seguente funzione miapush:

    sub miapush (\@@)
    {
        my ($array, @resto) = @_;
        push @$array, @resto;
    }

Altri caratteri usati nei prototipi sono $, per forzare un argomento scalare, % per indicare un hash (usato spesso come riferimento) e & per indicare un blocco di codice. Vedete perldoc perlsub per la documentazione completa.

Il Problema dei Prototipi

I prototipi modificano il modo in cui Perl fa il parsing del vostro codice e possono causare delle conversioni di tipo. Non servono né a documentare il numero o il tipo degli argomenti delle funzioni, né a mappare gli argomenti su parametri per nome.

Le coercizioni di tipo causate dai prototipi hanno comportamenti insidiosi, come quello di forzare il contesto scalare sugli argomenti:

    sub uguaglianza_numerica($$)
    {
        my ($sinistro, $destro) = @_;
        return $sinistro == $destro;
    }

    my @num = 1 .. 10;

    say 'Sono uguali, qualunque cosa questo significhi!'
        if uguaglianza_numerica @num, 10;

... ma funzionano solo con espressioni semplici:

    sub miapush(\@@);

    # errore di compilazione: prototipo non corrispondente
    # (assegnamento scalare dove ci si aspettava un array)
    miapush( my $elems = [], 1 .. 20 );

Per fare il debug di questo errore, gli utenti di miapush devono sapere che esiste un prototipo e conoscere le limitazioni dei prototipi di array. Peggio ancora, errori come questo sono tra i più semplici tra quelli causati dai prototipi.

Usi Appropriati dei Prototipi

Ci sono pochi casi in cui i vantaggi nell'uso dei prototipi superano i problemi, ma tali casi esistono.

Par prima cosa, i prototipi vi permettono di sovrascrivere le funzioni native. Dovete anzitutto verificare se è possibile sovrascrivere una funzione nativa esaminandone il prototipo con un semplice programma di test. Quindi, usate la direttiva subs per comunicare a Perl che intendete sovrascrivere una funzione nativa, e infine dichiarate la vostra sovrascrittura con il prototipo appropriato:

    use subs 'push';

    sub push (\@@) { ... }

Ricordate che la direttiva subs è attiva per tutto il resto del file, indipendentemente dagli scope lessicali.

La seconda ragione valida per usare i prototipi è per la definizione di costanti durante la compilazione. Quando Perl si imbatte in una funzione dichiarata con un prototipo vuoto (non senza prototipo) e tale funzione valuta a una singola espressione costante, l'ottimizzatore sostituisce tutte le chiamate alla funzione con costanti:

    sub PI_GRECO () { 4 * atan2(1, 1) }

Tutto il codice seguente usa il valore calcolato di pi greco al posto della bareword PI_GRECO o di una chiamata a PI_GRECO(), in base allo scope e alla visibilità.

La direttiva nativa constant gestisce questi dettagli al vostro posto. Il modulo CPAN Const::Fast crea delle costanti scalari che potete interpolare all'interno di stringhe.

Un uso ragionevole dei prototipi è quello di estendere la sintassi di Perl per operare su funzioni anonime in forma di blocchi. Il modulo CPAN Test::Exception sfrutta efficacemente questa possibilità per fornire una interessante API con computazione ritardata Vedete anche Test::Fatal. La sua funzione throws_ok() riceve tre argomenti: un blocco di codice da eseguire, un'espressione regolare di cui fare il match con la stringa dell'eccezione e una descrizione opzionale del test:

    use Test::More tests => 1;
    use Test::Exception;

    throws_ok
        { my $nonoggetto; $nonoggetto->regala() }
        qr/Can't call method "regala" on an undefined/,
        'Un metodo chiamato su un invocante non definito deve fallire';

La funzione esportata throws_ok() ha il prototipo &$;$. Il suo primo argomento è un blocco che diventa una funzione anonima. Il secondo argomento è uno scalare. Il terzo argomento è opzionale.

I lettori attenti avranno forse notato che manca la virgola dopo il blocco. Si tratta di una idiosincrasia del parser Perl 5, che si aspetta un carattere di spaziatura dopo un blocco con prototipo invece dell'operatore virgola. È un altro dei problemi della sintassi dei prototipi.

Potete usare throws_ok() senza usare i prototipi:

    use Test::More tests => 1;
    use Test::Exception;

    throws_ok(
        sub { my $nonoggetto; $nonoggetto->regala() },
        qr/Can't call method "regala" on an undefined/,
        'Un metodo chiamato su un invocante non definito deve fallire' );

Un ultimo caso di uso appropriato dei prototipi è quando definite una vostra funzione con nome da usare con sort Questo esempio è stato suggerito da Ben Tilly.:

    sub ordina_per_lunghezza ($$)
    {
        my ($sinistro, $destro) = @_;
        return length($sinistro) <=> length($destro);
    }

    my @ordinato = sort ordina_per_lunghezza @nonordinato;

Il prototipo $$ forza Perl a passare le coppie considerate da sort nell'array @_. La documentazione di sort segnala che questo metodo è un po' meno veloce di usare le variabili globali di package $a e $b, ma spesso l'uso di variabili lessicali compensa ampiamente la riduzione di velocità.

Equivalenza Metodi-Funzioni

Il sistema ad oggetti di Perl 5 è deliberatamente minimale (Riferimenti Blessed). Dato che una classe è un package, Perl non distinge tra una funzione e un metodo memorizzato in un package. La stessa istruzione sub viene usata per dichiarare entrambi. La documentazione può esplicitare le vostre intenzioni, ma Perl non avrà problemi a fare il dispatch ad una funzione invocata come metodo. Allo stesso modo, potete invocare un metodo come se fosse una funzione—qualificata, esportata o in forma di riferimento—se gli passate l'invocante manualmente.

Invocare una cosa sbagliata in modo sbagliato è causa di problemi.

Prospettiva del Chiamante

Considerate una classe con svariati metodi:

    package Ordine;

    use List::Util 'sum';

    ...

    sub calcola_prezzo
    {
        my $self = shift;
        return sum( 0, $self->articoli_ordinati() );
    }

Dato un oggetto Ordine $o, le seguenti chiamate di questo metodo possono sembrare equivalenti:

    my $prezzo = $o->calcola_prezzo();

    # sbagliato; non usatelo
    my $prezzo = Ordine::calcola_prezzo( $o );

Sebbene in questo semplice caso esse producano lo stesso output, la seconda viola l'incapsulamento dell'oggetto aggirando la ricerca del metodo.

Se $o fosse invece una sottoclasse o un allomorfismo (Ruoli) di Ordine che sovrascrive calcola_prezzo(), aggirare il dispatch del metodo chiamerebbe il metodo sbagliato. Qualunque modifica dell'implementazione di calcola_prezzo(), ad esempio relativa all'ereditarietà o alla delega con AUTOLOAD()—potrebbe far fallire il codice chiamante.

Esiste una situazione in Perl in cui questo comportamento può sembrare necessario. Se forzate la risoluzione senza dispatch di un metodo, come invocate il riferimento a metodo risultante?

    my $rif_a_met = $o->can( 'applica_sconto' );

In realtà, avete due possibilità. La prima consiste nello scartare il valore di ritorno del metodo can():

    $o->applica_sconto() if $o->can( 'applica_sconto' );

La seconda consiste nell'usare il riferimento stesso insieme alla sintassi di invocazione di metodo:

    if (my $rif_a_met = $o->can( 'applica_sconto' ))
    {
        $o->$rif_a_met();
    }

Quando $rif_a_met contiene un riferimento a funzione, Perl fa un'invocazione su tale riferimento passando $o come invocante. Questa sintassi funziona anche quando sono attive delle restrizioni, in modo analogo all'invocazione di un metodo attraverso uno scalare che ne contiene il nome:

    my $nome = 'applica_sconto';
    $o->$nome();

C'è un piccolo svantaggio nell'invocare un metodo per riferimento; se la struttura del programma cambia tra il momento in cui il riferimento viene memorizzato e il momento in cui viene usato per l'invocazione, tale riferimento potrebbe non corrispondere più al metodo appropriato. Se la classe Ordine fosse cambiata in modo tale che Ordine::applica_sconto non fosse più il metodo giusto da chiamare, il riferimento in $rif_a_met non sarebbe stato aggiornato.

Quando usate questa forma di invocazione, limitate lo scope dei riferimenti.

Prospettiva del Chiamato

Dato che Perl 5 non distingue tra funzioni e metodi nelle dichiarazioni e dato che è possibile (per quanto sconsigliabile) invocare una data funzione sia come funzione che come metodo, si può scrivere una funzione pronta ad essere chiamata in entrambi i modi. Il modulo CGI distribuito con Perl da il cattivo esempio. Le sue funzioni applicano svariate euristiche per determinare se il loro primo argumento è un invocante.

Ciò ha molti svantaggi. Prima di tutto, è difficile predire esattamente quali sono gli invocanti potenzialmente validi per un dato metodo, specialmente quando è possibile avere a che fare con delle sottoclassi. Anche creare una API che non possa essere usata scorrettamente dagli utenti è più difficile, così come scrivere la documentazione. Che cosa succede quando una parte del progetto usa l'interfaccia procedurale e un'altra parte usa l'interfaccia a oggetti?

Se dovete fornire sia un'interfaccia procedurale che una orientata agli oggetti ad una libreria, create due API separate.

Tie

Mentre l'overloading (Overloading) vi permette di personalizzare il comportamento di classi e oggetti per tipi specifici di coercizione, un meccanismo detto tying (legatura, NdT) ci permette di personalizzare il comportamento dei tipi nativi (scalari, array, hash e filehandle). Qualunque operazione effettuata su una variabile legata si traduce in una specifica chiamata di metodo.

La funzione nativa tie (lega, NdT) permetteva originariamente di usare spazio su disco come memoria di supporto degli hash, in modo che Perl potesse accedere a file più grandi di quanto potesse essere ospitato in memoria. Il modulo Tie::File distribuito con Perl fornisce un sistema simile e vi permette di trattare i file come se fossero array.

La classe a cui fate il tie di una variabile deve rispettare una interfaccia predefinita per ogni tipo di dato. Vedete perldoc perltie per una panoramica, prima di consultare i moduli Tie::StdScalar, Tie::StdArray e Tie::StdHash della distribuzione Perl per i dettagli più specifici. Iniziate ereditando da una di tali classi, e sovrascrivete i metodi che dovete modificare.

Collisioni tra Nomi di Classe e di Package

Come se tie non creasse già abbastanza confusione, Tie::Scalar, Tie::Array e Tie::Hash definiscono le interfacce necessarie per legare scalari, array e hash, mentre Tie::StdScalar, Tie::StdArray e Tie::StdHash forniscono le implementazioni di default.

Legare Variabili

per legare una variabile:

    use Tie::File;
    tie my @file, 'Tie::File', @arg;

Il primo argomento è la variabile da legare, il secondo è il nome della classe a cui legarla e @args è una lista opzionale di argomenti richiesti dalla funzione di legatura. Nel caso di Tie::File, il parametro richiesto è un nome valido di file.

Le funzioni di legatora sono simili a costruttori: TIESCALAR, TIEARRAY(), TIEHASH() e TIEHANDLE() rispettivamente per gli scalari, gli array, gli hash e i filehandle. Ciascuna funzione restituisce un nuovo oggetto che rappresenta la variabile legata. Entrambe le funzioni native tie e tied restituiscono tale oggetto. La maggior parte delle persone, tuttavia, usa tied in contesto booleano.

Implementazione di Variabili Legate

Per implementare la classe di una variabile legata, ereditate anzitutto da uno dei moduli distribuiti con Perl come Tie::StdScalar Tie::StdScalar non ha un proprio file .pm associato, perciò dovete usare Tie::Scalar per renderlo disponibile., quindi sovrascrivete i metodi specifici relativi alle operazioni che volete modificare. Nel caso di uno scalare legato, è probabile che essi includano FETCH e STORE, forse anche TIESCALAR(), ma probabilmente non DESTROY().

Con poco codice, potete creare una classe che tiene traccia di tutte le letture e le scritture su uno scalare:

    package Tie::Scalar::Registra
    {
        use Modern::Perl;

        use Tie::Scalar;
        use parent -norequire => 'Tie::StdScalar';

        sub STORE
        {
            my ($self, $valore) = @_;
            Registro->scrivi("Memorizzo <$valore> (prima era [$$self])", 1);
            $$self = $valore;
        }

        sub FETCH
        {
            my $self = shift;
            Registro->scrivi("Leggo <$$self>", 1);
            return $$self;
        }
    }

    1;

Assumete che il metodo scrivi() della classe Registro riceva una stringa e il numero di contesti di chiamata nello stack di cui riportare il numero di linea.

I metodi STORE() e FETCH() trattano $self come uno scalare blessed. Assegnare un valore a tale riferimento a scalare cambia il valore dello scalare e leggere da esso restituisce il valore dello scalare.

Analogamente, i metodi di Tie::StdArray e Tie::StdHash operano rispettivamente su dei riferimenti blessed ad array e hash. La documentazione contenuta in perldoc perltie spiega i numerosi metodi supportati da queste classi, ad esempio per leggere e scrivere valori multipli.

Non è Vero che tie è Divertente?

Grazie all'opzione -norequire la direttiva parent evita di tentare di caricare un file per Tie::StdScalar, dato che tale modulo è contenuto in una parte del file Tie/Scalar.pm.

Quando usare le Variabili Legate

Le variabili legate possono sembrare delle divertenti occasioni per mostrare la propria brillantezza, ma spesso producono interfacce confuse. A meno che abbiate delle ragioni molto valide per volere che certi oggetti si comportino come se fossero tipi di dati nativi, evitate di creare le vostre legature. Usare tie è anche molto più lento che usare i tipi nativi a causa di vari aspetti implementativi.

Tra le ragioni valide ci sono quella di facilitare il debugging (ad esempio usare lo scalare tracciato per aiutarvi a capire dove cambia il suo valore) e quella di rendere possibili certe operazioni altrimenti impossibili (ad esempio accedere a file di grandi dimensioni con un uso efficiente della memoria). Le variabili legate sono invece difficilmente utili come interfacce primarie agli oggetti; spesso è troppo difficile e vincolante tentare di uniformare l'intera interfaccia a quella supportata da tie().

Un ultimo avvertimento poco piacevole ma convinente; troppo codice devia dai suoi obiettivi al fine di prevenire l'uso di variabili legate, spesso accidentalmente. Questo è sicuramente un problema, ma la violazione delle aspettative del codice di una libreria rivela normalmente dei bug che non è in vostro potere risolvere.