sabato 21 gennaio 2017

Da 5 a 6: i sigilli in Perl 6

Una delle prime difficoltà mentali che ho incontrato nell'apprendere Perl 6 è stata la variazione dell'uso dei sigilli, e non perché fosse difficoltosa in sé (anzi risulta semplificata!), ma perché mi appariva incoerente.
In Perl 5 era abbastanza facile, almeno per me, comprendere che $ indicava "un singolo elemento", fosse questo un valore di array, hash o un riferimento (ad un oggetto, sub o altro).
In Perl 6 la cosa non è così e infatti il sigillo del tipo di "container" rimane invariato per tutto il ciclo di vita, e questo significa che ad esempio un array avrà sempre @ come sigillo anche quando si vuole accedere ad un singolo elemento di questo.
Per maggiori dettagli si legga ad esempio "A sigil is for life, not just for value type" nella Exegis 2 .
La cosa che inizialmente mi disturbava era che il sigillo $ era in un certo senso "sovraccaricato": funzionava per i normali scalari (es. stringhe, numeri) e anche per gli oggetti. Si noti che questo è esattamente il comportamento che si ha in Perl 5, quindi perché scomodarsi a variare i sigilli degli altri cointainers?
La spiegazione mentale che mi sono dato è intrinseca al fatto che in Perl 6 tutto è un oggetto (un po' come in Ruby): anche le stringhe sono oggetti, così come i numeri, ecc.

% perl6

> "hello".^name
Str
> 10.^name
Int

In un certo senso questo significa che il sigillo $ viene usato come "singolo oggetto", non piu' come "scalare primitivo o oggetto", perché di fatto non esistono piu' "scalari primitivi".

E la pace è tornata nella mia mente!

giovedì 19 gennaio 2017

Perl 5 e le eccezioni: come funziona Try::Tiny

Perl 5 non prevede un meccanismo di gestione del flusso delle eccezioni, pur prevedendo queste ultime, come altri linguaggi fanno, spesso ereditando da C++ i blocchi e gli operatori try/catch e similari.
In Perl5 le eccezioni sono riportate come variabili globali, in particolare:

  • $! (errno) contiene la versione numerica o stringa della variabile globale C-Unix errno(3);
  • $? (child error) lo stato riportato dalla exit dell'ultimo sottoprocesso creato per pipe, backtick, chiamata a system (simile in questo alla stessa variabile della bourne shell);
  • $@ (eval error) lo stato di errore dell'ultimo blocco eval eseguito.

Unitamente a questo, Perl 5 prevede la funzione speciale "die" che solleva un'eccezione: se questo avviene nel programma principale questo termina (muore, da qui il nome), se invece si è in un blocco eval il blocco termina e la variabile globale $@ viene impostata a quanto specificato nell'eccezione. Da notare che è possibile passare anche un riferimento a "die", quindi anche un oggetto (eccezione) che viene poi assegnato alla variabile $@.

L'uso di variabili globali per la gestione delle eccezioni non è certo ottimale, almeno nella programmazione OOP. Si rischia infatti che il sollevamento di una eccezione, in blocco annidato, faccia sollevare una nuova eccezione e quindi che la propagazione sia a piu' livelli e con cause differenti. Essendo $@ unica, solo l'ultimo livello di propagazione viene riportato. Non solo: essendo $@ globale si rischia che il suo valore sia resettato in punti inaspettati e quindi è necessario "ricordarsi" di analizzare $@ quanto prima possibile.


Sono quindi nati molti moduli su CPAN per la gestione delle eccezioni, e uno del quale parlare è Try::Tiny.
Questo è un modulo molto semplice, non gestisce alcuni casi particolari (es. return in eval), ma fornisce comunque una sintassi "carina" e che ogni programmatore riesce a comprendere:

try{
die "Argh!";
}
catch { say "Eccezione: $_"; };

In particolare Try::Tiny imposta la variabile topic a $@ appena si entra nel blocco catch, permettendo quindi di dimenticarsi di $@ stessa. Ma come riesce a fare la magia della sintassi e a introdurre try-catch? Beh, ancora una volta entra in gioco la magia e la flessibilità di Perl 5.

Anzitutto si noti il ';' al termine del blocco catch: questo indica che in realtà si sta eseguendo una istruzione Perl 5 valida, o per meglio dire una funzione. E infatti Try::Tiny esporta tre funzioni che si chiamano "try", "catch" e "finally".
Si consideri un estratto di "catch" prima:

sub catch (&;@) {
  my ( $block, @rest ) = @_;

  croak 'Useless bare catch()' unless wantarray;

...
  return (
   bless(\$block, 'Try::Tiny::Catch'),
        @rest,
  );
}


Anzitutto la funzione accetta come primo parametro un blocco di codice o una sub (il carattere & del prototipo), e si aspetta di essere chiamato in contesto di lista, quindi ad esempio come

my ($catch) = ( catch { say "Eccezione: $_"; } );

La fine della funzione catch converte il blocco di codice in un oggetto di tipo 'Try::Tiny::Catch', e questo serve per dei controlli interni.

Vediamo brevemente la funzione "try", sicuramente piu' interessante:


sub try (&;@) {
  my ( $try, @code_refs ) = @_;
  my $wantarray = wantarray;
  my ( $catch, @finally ) = ();


  foreach my $code_ref (@code_refs) {

   if ( ref($code_ref) eq 'Try::Tiny::Catch' ) {
      croak 'A try() may not be followed by multiple catch() blocks'
     if $catch;
    $catch = ${$code_ref};
  } elsif ( ref($code_ref) eq 'Try::Tiny::Finally' ) {
     push @finally, ${$code_ref};
  } else {
    croak(
    'try() encountered an unexpected argument ('
       . ( defined $code_ref ? $code_ref : 'undef' )
.      ') - perhaps a missing semi-colon before or'
    );
  }
}

...

  my $failed = not eval {
  $@ = $prev_error;

    # evaluate the try block in the correct context
    if ( $wantarray ) {
      @ret = $try->();
    } elsif ( defined $wantarray ) {
      $ret[0] = $try->();
   } else {
      $try->();
  };

  return 1; # properly set $failed to false
};


  $error = $@;
  $@ = $prev_error;


  if ( $failed ) {
   ...
    if ( $catch ) {

     for ($error) {
      return $catch->($error);
    }

 }

  return;
 } else {
  return $wantarray ? @ret : $ret[0];
 }
}

Anche qui la funzione si aspetta come primo argomento un blocco o una sub. Gli altri argomenti vengono trattati a loro volta come argomenti o sub, e si controlla nel primo loop che siano di tipo 'Try::Tiny::Catch', o finally o vengono considerati come alieni.
Successivamente si esegue l'eval del blocco passato come primo argomento e, qualora questo abbia impostato un errore, si esegue anche il blocco catch passanto la variabile di errore come primo argomento.

Chiarito come avviene la magia sintattica di try-catch è possibile anche scrivere il blocco esplicitando le funzioni e i loro argomenti:

try( sub { die "Eccezione!" }, 
     catch( sub { say "Presa: $_" } ) );

Ma come vengono gestiti i blocchi finally? Il meccanismo è abbastanza elegante: all'interno della funzione "try" vengono creati degli oggetti (blessed reference) per ogni blocco finally incontrato. Tali oggetti hanno un metodo DESTROY che invoca il code ref (blocco o funzione) specificato. Quando la funzione try termina gli oggetti vanno fuori visibilità e quindi vengono distrutti, e di conseguenza viene eseguito DESTROY che a sua volta richiama il codice specificato dall'utente.

mercoledì 18 gennaio 2017

Quanto ITPUG? (Le mie dimissioni da ITPUG parte 2)

Avendo marcato la fine della mia attività da consigliere con le dimissioni di circa un mese fà, ho deciso, quasi per curiosità, di cercare di quantificare il lavoro svolto da ITPUG e dal suo consiglio negli ultimi due mandati.
Si tratta di dati ovviamente indicativi, visto che strumenti diversi offrono opzioni di statistica differenti, ma possono essere utilizzati per un grezzo lavoro di analisi quantitativa.
E' bene sottolinearlo: si parla di attività quantitativa, non qualitativa!
Tuttavia l'attività quantitativa spesso indica e sottointende la presenza in associazione e la vitalità della stessa, da qui il mio interesse per questi semplici dati.

Ovviamente non sto svelando alcun segreto, questi dati sono comunque visibili e calcolabili da ogni consigliere e socio, con un po' di impegno e pazienza. Potrei anche aver commesso qualche errore di computazione, nel qual caso ogni correzione è ben accetta.

Considerando quindi la data del 30 aprile come termine di un biennio (e il relativo inizio del successivo), e sottolineando come il biennio 2015-2017 non sia ancora giunto al termine (e quindi i dati di tali biennio si riferiscono alla data attuale), si ha che:
  • biennio 2013-2015
  1.  301 commits
  2. 281 tickets
  3. 19 verbali riunioni di consiglio
  4. 108 thread in lista itpug-consiglio@
  5. 170 thread in lista itpug-soci@
  • biennio 2015-2017
  1. 103 commits
  2. 190 tickets
  3. 7 verbali riunioni di consiglio
  4. 160 thread in lista itpug-consiglio@ 
  5. 130 thread in lista itpug-soci@

L'attività del consiglio può essere quantificata con il numero di commits nel repository documentale, ovvero quanti documenti il consiglio ha ritenuto di inserire fra quelli ufficiali (fra questi, i verbali delle riunioni di consiglio), nonché dal numero ti tickets (ovvero di problematiche e task da affrontare). Come si può notare il valore di entrambi è drasticamente calato nell'ultimo biennio, segno che non si utilizza piu' né il repository né il sistema di ticketing come strumento base per la gestione delle attività del consiglio.
Questo è in parte confermato anche dall'aumento del numero di thread nella mailing list consiglio, che rispecchia il maggior uso e preferenza di questo canale di discussione rispetto agli altri strumenti.
A mio avviso questa è una regressione, poiché l'email non rappresenta lo strumento ideale per gestire scadenze, priorità, condivione di documenti.
Il dato però piu' allarmante, a mio avviso, è quello delle riunioni di consiglio, o meglio, dei verbali delle riunioni di consiglio. Come si può vedere le riunioni di consiglio sono scese del 60% circa, segno che il consiglio non ritiene sufficientemente utile riunirsi con regolarità (ulteriore conferma della preferenza del canale e-mail), o ha delle difficoltà a riunirsi.
Appare anche diminuita l'attività e la presenza del consglio nella mailing list dei soci, visto che il numero di thread si è abbassato. Ora, questo dato in particolare non coinvolge direttamente il consiglio, visto che i thread possono anche essere creati dai singoli soci (e anzi, questo è quello che dovrebbe accadere); tuttavia l'abbassamento del numero di discussioni evidenzia, secondo me, un raffreddamento del "networking" fra i soci al quale il consiglio dovrebbe cercare di porre rimedio.

Lascio ad altri il computo dei post e della frequenza di aggiornamento del planet italiano perché sarei sicuramente male interpretato.

ITPUG ha una struttura di supporto informatico sicuramente complessa e funzionale (si veda qui), di gran lunga superiore a quella di molte altre associazioni PostgreSQL analoghe. Ritengo sia di vitale importanza per l'associazione che ogni consiglio riesca ad usare al meglio questa infrastruttura, nonché la storia che essa contiene (archivi, logs, ecc.) poiché rappresenta un bagaglio culturale di enorme valore.

GNU tar e l'ordine delle opzioni

Passando dalla versione di GNU tar 1.25 alla versione piu' recente 1.29 sono incappato in un piccolo e subdolo problema: la sintassi del comando è leggermente cambiata (orrore!).
In particolare la clausola --exclude deve essere specificata prima del file da archiviare (ma ovviamente dopo l'archivio stesso), altrimenti non verra' onorata e l'archivio conterrà file non voluti.
Quindi la sintassi:

tar cjvf archivio.tar.bz2 . --exclude=db

accettata e valida per GNU tar 1.25, deve essere riscritta per GNU tar 1.29 come

tar cjvf archivio.tar.bz2 --exclude=db .

Ora, considerando che essendo una opzione (--) facilmente riconoscibile, si poteva almeno inserire un warning per la sintassi errata!

martedì 17 gennaio 2017

Perl e gli operatori logici a differente priorità

Alcune difficoltà che noto in molti colleghi che iniziano a programmare in Perl venendo da altri linguaggi è il concetto di contesto, la differenza fra array e liste e gli operatori a bassa e alta priorità. Con riferimento a questi ultimi mi sono reso conto di non aver mai pensato realmente al loro utilizzo, basandomi per lo piu' sul fatto che "Perl farà la cosa giusta".

Per comprendere meglio la differenza fra "or" e "and" a bassa priorità e i loro duali "||" e "&&" ad alta priorità si consideri il seguente pezzetto di codice:

my $x = 10;
my $y = 20;

my $z = $x and $y;
say "Bassa priorita' = $z";
$z = $x && $y;
say "Alta priorita' = $z";

che produce come output

Bassa priorita' = 10
Alta priorita' = 20


Anzitutto utilizzo un and-logico con il primo valore ($x) non falso per evitare che sia cortocircuitato. Inoltre si tenga presente che, a differenza di altri linguaggi, gli operatori logici di cui sopra restituiscono l'ultimo valore valutato (Perl farà la cosa giusta...), che significa che prese singolarmente le due righe di codice seguente

$x and $y;
$x && $y;

restituiscono entrambe il valore $y. Ma l'and verbale "and" ha bassa priorità, e questo significa che viene applicato successivamente ad altri operatori, nell'esempio di cui sopra dopo l'operatore di assegnamento "=". Viceversa l'and-logico simbolico viene applicato prima di altri operatori, ovvero aggiungendo un po' di parentesi il programma di cui sopra è come se fosse valutato così:

( my $z = $x ) and $y;
$z = ( $x && $y );


Ecco un esempio piu' utile:

sub value{ my ( $value ) = @_; $value; }
$z = value( 0 ) or $y;
say "Bassa priorita' = $z";
$z = value( 0 ) || $y;
say "Alta priorita' = $z";


che produce il risultato

Bassa priorita' = 0
Alta priorita' = 20

subito.it e i compratori a "ondate"

Ho notato un trend particolare negli annunci che pubblico su subito.it: si verifica a ondata la richiesta per lo stesso pezzo/articolo e in un lasso di tempo spesso molto breve.
Vado a spiegare meglio.
Immaginiamo di mettere in vendita un articolo con i suoi accessori, ad esempio una macchina fotogratica con i relativi obiettivi. Ebbene si verifica che, negli stessi giorni, e comunque in un lasso di tempo che non supera mai le due settimane, vi siano piu' compratori interessati ad un particolare pezzo (ad esempio il flash) e solo a quel pezzo.
Questo mi ha portato a riflettere sul fatto che vi sia un possibile mercato parallelo ove vengono pubblicate ricerche di un determinato pezzo (nel mio esempio il flash) e quindi alcuni intermediari cerchino di accaparrarsi su subito.it il pezzo in questione per poi rivenderlo.
Un'altra teoria potrebbe essere che in club, gruppi di discussione, social network, un annuncio abbia maggiore risalto e tutti si accaniscano (magari dopo una discussione interna) per accaparrarsi uno dei pezzi in vendita.

Si tratta ovviamente solo di teorie e congetture, ma nella mia esperienza mi capita sempre piu' spesso di assistere a vere e proprie ondate di acquirenti sempre su quel singolo pezzo che magari è esposto da settimane o mesi.

Coincidenze?

sabato 14 gennaio 2017

Eclipse Neon, Tomcat 7 e il nome del server non editabile...

Sono inciampato in un piccolo bug di Eclipse Neon 1: ero impossibilitato a creare qualunque server Tomcat 7 perchè non mi era possibile inserire il nome del server, come invece avveniva correttamente per le altre versioni di Tomcat (sia inferiori che maggiori).
La soluzione, nel mio caso, è stata quella di rimuovere i due file

org.eclipse.jst.server.tomcat.core.prefs
org.eclipse.wst.server.core.prefs

dalla directory .metadata/.plugins/org.eclipse.core.runtime/.settings dentro al workspace.
Ovviamente tutto fatto ad Eclipse spento (e con backup di tali file).