venerdì 14 aprile 2017

A baby Perl program from a backup in the attic!

…I was assigned one of my very first program to develop: a way to assure an user can run no more than a specific number of instances
of the same executable.

The real story was this: a company was using an Unix ncurses based ERP, and users were forced to connect via remote shell and
launch it automatically. The problem was that several users were opening more terminals (and therefore ERP clients) limiting
resources to other users.

I wanted to demonstrate my skills being able to implement the solution in a couple of hours, and instead of spending time
searching for a production-ready solution on the Internet, I decided to do it all on my own.

It was clear to me I was needing a kind of wrapper to control the execution of external programs. In fact, since users were
automatically switched from the remote shell into the ERP client, it did suffice to place the wrapper as the
user shell.

But how to implement it? At that time I knew Java, C++, C, Perl and some Bourne Shell. Making it in Java was too heavy, and
most notably required to install a JVM on the server (it was the time of the Red Hat 5 branch). C and C++ required too much time
to come with a working solution, and Perl was the most promising allowing me to do everything I was able to do with
other languages, overcoming the limitation of the shell (e.g., arithmetic).

But at that time I was not a fluent Perl developer.

A few days ago I found the above program out of an old backup, and so I spent five minutes to read it again. Not surprisingly, I found
it awful with respect to the code I write today, and moreover I see a lot of code uglyness due to baby steps. But hey, it was fun
to see how many things I've changed in my coding style, idioms, adoption of the language and of its operators, and most notably
about how I'm today toward compact code instead of long and blown code. At that time I was convinced that as long the code was, as much
it must be simpler to read, today I believe it is the opposite (with a very few exceptions I tend not to adopt until I
can remember very well).

In this article I will dissect a few parts of the above program to show you how I was writing code back in those days, and it was
a huge time ago (at least in computer timeline): 15+ years ago.

2 Baby Steps

Well, as you can imagine when I started doing a real job I was nothing more than a programmer-to-be, and my knowlegde
about Perl was really tiny. I mean, I read the Camel Book (of course!), and I was experimenting with Perl as much as I could,
but I was not an avid consumer of the Perl community (as well as other communities) and so I did not have a lot of chances
to learn from other people experience and code.

More than that, I just finished my university degree, and so my mind was literally full of theories about writing
correct and beautiful code, but that were just theories without any implementation! And last but not least, I was convinced
I could do everything by my own (that is, who needs modules?).

Today I recognize the following as baby steps in the Perl world.

2.1 Method Prototypes

Yes, I was using prototypes everywhere.

Why? I suspect they were comfortable to me, since I was used to languages where each method has a prototype (C, C++, Java), and
so I was trying to use a kind of common approach to every language, applying to Perl prototypes even when I was not needing
them at all!

Of course, by time and experience, I found that prototypes are usually not useful at all in my programs, and made refactoring
a lot harder.

2.2 Method instead of Operators

Perl allows operators to be used without braces, but that was something my eyes were not able to parse!
Again, coming from languages where methods and operators both need parentheses, I tried to bend Perl to my will
and use operators the very same way.

2.3 Untrusting the Thruth

Perl has the great ability to cast a variable depending on the context, but it was something too much complex for me.
So for instances, instead of testing a scalar against a not-true value, I was using the defined operator:

# if ( $scalar )
if ( defined $scalar ){ ... }

Worst: I was not using the ability of an array to be scalar-contextualized in tests:

#if ( ! @array )
if ( not defined @array || $#array <= 0 ){ ... }

2.4 Global Variables

my what?

I was not using scoped variables, and the reasons were:

  1. it was not clear to me the real difference between my and local, and I have to confess I thought local was what should be the
    name for scoped variables (maybe because of the way some shells declare local variables), but since local was usually a bad idea
    I decided not to use either;
  2. my scripts were quite small and self contained, so there was no risk at all about variable clash.

2.5 Backtick as a Rescue!

I knew pretty much well a kind of command line Unix tools (e.g., grep, awk, sed, find), and sometimes I needed
to do something in Perl without knowing how to do but with shell tools. So, how could I merge the both?

Easy pal: backtick the Unix tools and manage the result in Perl!

2.6 No Modules, please

I was not using modules, both by design and by fear.

With by design I mean that I was writing scripts for machine loosely connected to the Internet (just think I was working with server behind an ISDN router!),
and therefore even dowloading a module and install it could be quite a pain.

By fear means I was quite scared about importing other people's code. I was still learning about how to use correctly the CPAN, how to read
the documentation, and so on. And I did not want to install things in the wrong way on code that must run. After all, I was half-an-hour
away from implementing some kind of module functionality by my own (ah, the ego…).

2.7 Regexp Sometimes…

I tried to use regexp, but I was too less experienced. Therefore I usually did the substitution with friendly tools, like
split and alike.

I fear regexp nomore, and even if I'm not a master at all, I find interesting placing them in programs not only because
they allow me to write clear and compact code, but also because they still are some criptic stuff other developers
have hard time reading.

3 The Code

3.1 University Distortion

There are a couple of places where you can see evidence of the teachings at my university, most notably the
command line argument loop:

sub parseCommandLine()

    # The script must be at least two arguments.
    if ( $#ARGV < 1 or $#ARGV > 4 )


    foreach $argument (@ARGV)
        if ( $argument =~  /(-program=)/ )
            $argument =~ s/(-program=)//;
            $PROGRAM = $argument;
        elsif ( $argument =~ /(-policy=)/ )
            $argument =~ s/(-policy=)//;
            $POLICY_FILE = $argument;
        elsif ( $argument =~ /(-username=)/ )
            $argument =~ s/(-username=)//;
            $USERNAME = $argument;
        elsif ( $argument =~ /(-message=)/ )
            $argument =~ s/(-message=)//;
            $MESSAGE_FILE = $argument;

    # check main parameters
    if ( not defined $USERNAME or $USERNAME eq "")
        warn("\nCannot find username !!\n\n");
    elsif ( not defined $PROGRAM or $PROGRAM eq "")
        warn("\nCannot find the program name \n\n");
    elsif ( not defined $POLICY_FILE or $POLICY_FILE eq "")
        warn("\nI need a policy file to run!\n\n");


As you can see I was hardwiring the arguments directly into the program, as well as I was not using any getopt-like module.
That was by culture: university never told me there was a getopt way of getting parameters!

Please note also that I was checking the argument numbers as well as exiting from the program with a different exit code for each branch.
The style reminds pretty much the C-style I was used to work with during my exams.

3.2 Ready? Go!

How can I execute another program from within the Perl wrapper?

Well, the simplest way that I was aware of is to call execute, in other words, to work as a low level C:

sub launch($)
    # take arguments
    my ($program) = @_;

    # execute the program

3.3 How Many Instances can you Run?

How to define how many instances an user can run?

Here I decided to take inspiration from the sudo policy file, and so I invented my own policy file with a pretty
simple syntax:



  • username was the Unix username;
  • executable was the name of the executable to run;
  • instances was the number of the maximum allowed instances of the executable for the specified username.

And to make it looking mor eprofessional, I allowed the policy file to include a star in any field
in order to mean anything or any instance.

Of course, the above was full crap. For instance, if you were able to create a link from the executable to another
with the right name you could change you allowance. But luckily, no user was able to do that, and to some
extent even the other sysadmins!

Having defined the policy file syntax, reading the file was as simple as:

sub getAllowedInstances($$$)
    # take params
    my ($policy_file, $username, $program) = @_;
    my $instances = -1;

    # try to open the policy file
    open (POLICY_FILE_HANDLER, "<".$policy_file) || die("\nCannot parse policy file <$policy_file> !\n");
    print "\nParsing configuration file $policy_file for program $program, username $username...";

    $instances = 0;           # by default do not allow a user to execute a program

    # get each line from the policy file, and then search for the username
    while ( $line = <POLICY_FILE_HANDLER>  )

        print "\n\t Configuration line: $line ";

        # take only lines with the program name specified
        if( grep("$program", $line) ){

            @lineParts = split("@",$line);
            $configUsername = $lineParts[0];
            print "\ncontrollo se $username == $configUsername";

            if ( $username eq $configUsername )
                # this is the right line
                # take the instances number
                @pieces = split("=" , $line);
                $instances = $pieces[1];
                # remove the '\n'
                print "\n\t\t\tUser allowance: $instances";
                return $instances;
            elsif ( $configUsername eq "*" )
                # a valid entry for all users
                # take the instances number
                @pieces = split("=" , $line);
                $instances = $pieces[1];
                # remove the '\n'
                print "\n\t\t\tGlobal allowance: $instances";

    # no lines found, the user has no restrictions
    return $instances;

What an amazing piece of code, uh?

Instead of reading the policy file once (or seldom), I was reading it fully every time I needed to check an user; it was
a very non-optimzed code.
Moreover, I was reading and grepping it a line at time, a very keyboard effort.

3.4 How much are you Running?

How to get the number of runnable instances an user was running?

Piece of cake: let's do it in Unix, backtick in Perl and increase by one:

sub getRunningInstancesCount($$)
    # get parameters
    my ( $program, $username ) = @_;

    # get all processes for this program and the username
    @processes = `ps -u $username | grep $program | grep -v "grep"`;

    # get all lines count
    $running_instances = 0;
    if ( $#processes >= 0 )
        $running_instances = $#processes + 1;
        return 0;

    return $running_instances;

Please note also the adoption of literals instead of variables: for example return 0; instead of return $running_instances where
the latter has been already initialized to zero.

3.5 All Pieces Together

Now, putting the places together made the main loop as simple as:

print "\nYou're running $RUNNING_INSTANCES $BASE_NAME\n";

# if the user can, launch the program
    print "\nExecuting $PROGRAM...";
    print "\nBye!\n";
    print "\nYou can't run no more instances of $PROGRAM";
    print "\nBye\n";

4 Lesson Learned

There is no lesson to learn here, but a lot of lessons learned in the between.

The only thing I could say is that you should never, never, throw away your code. Keep it, it is cheap, and someday you could
see how you progressed from a green programmer to the very guru developer you probably are today.

Dimensioni delle tabelle e dei dati dump di testo, qualche insignificante esperimento

Mi sono ritrovato per le mani una vecchia e obsoleta istanza PostgreSQL 8.4 piuttosto grossa, lo spazio disco del tablespace
risultava essere di circa 13 GB! Ho quindi preso spunto per fare una piccola indagine su cosa occupasse tanto spazio,
concentrandomi solo sulle relazioni (tabelle):

SELECT c.oid,nspname AS table_schema
      , relname AS TABLE_NAME
      , c.reltuples AS row_estimate
      , to_char( pg_total_relation_size(c.oid)::real/(1024*1024), '99G999D99' ) AS MB
FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
      relkind = 'r'
      nspname = 'public'

Ebbene sono saltate subito all'occhio tre tabelle in particolari (nomi di fantasia):

  oid   | table_schema | table_name | row_estimate |     mb
   63740 | public       | tab1      |  8.74153e+06 |   2.248,58
   66161 | public       | tab2      |   2.9728e+06 |   1.192,00
   65032 | public       | tab3      |  2.44735e+06 |   1.280,77

Come si nota queste tre tabelle superano di slancio ciascuna una occupazione di 1 GB, arrivando fino a 9 milioni di tuple circa!
Insomma, non una cosa eccezionale per PostgreSQL, ma sicuramente nemmeno una cosa di routine, e che comunque indica forse la necessità
di una riprogettazione o di un partitioning.
Comunque, le stesse tabelle su disco quando occuperebbero in formato testo?

% pg_dump -h localhost -U luca -t tab1 testdb > tab1.sql

Effettuando il dump, con molta pazienza, di quelle tre tabelle sopra indicate si ottiene che:

% ls -lh tab?.sql
-rw-r--r-- 1 luca luca 579M 2017-04-12 11:32 tab1.sql
-rw-r--r-- 1 luca luca 494M 2017-04-12 11:37 tab2.sql
-rw-r--r-- 1 luca luca 571M 2017-04-12 11:36 tab3.sql

e quindi lo spazio occupato all'interno di PostgreSQL risulta da 2 a 4 volte superiore allo spazio disco dei dati testuali.
Chiaramente questa non rappresenta una inefficienza di PostgreSQL, quanto una naturale esigenza del database di tenere i dati allineati,
avere le pagine dati (8kB) con spazio sufficiente per consentire aggiornamenti, ecc.

Se si effettua un vacuum full sulle tabelle di cui sopra si ottiene il seguente risultato:

> vacuum full verbose tab1;
0 index pages have been deleted, 0 are currently reusable.

ad indicare che il database era già "buono", e abbastanza compattato. Ovviamente i dati che PostgreSQL riporta sul numero di tuple e dimensione
dei dati sono rimaste invariate.

lunedì 10 aprile 2017

On learning thru Open Source Software

I read an interesting blog post on adopting open source at university as a way of teaching computer science, and I posted also a comment
on such article.
Here I would like to extend my point of view about the subject.
Having worked in an university for a few years, having done computer science at both sides of the desk, and having a quite good
experience in what became my day-to-day work, I believe I can express some opinion about.

Adopting open source as a teaching methodology should just be.
Instead of asking students to produce the same crappy software all over the time (a database layer, a GUI layer, a music store,
a calculator), put them on a real piece of software that someone in the world could use and adopt.
  1. motivation: students will be happy to work on some real stuff with the main author(s) thanking them for their help, time,
    and work;
  2. learn something useful: real programs do things in the real world, and things in the real world must work, and work fast, and work
    in accurate way. That is:
    • work on real data: nobody will ever notice your crappy homework do a full table scan each time you need to display one of your
      fake music titles out of ten, but a real program will freeze once you try to do a full table scan just to display out a detail
      out of a million records. This is something university cannot teach you, trust me!
    • deal with problems: university will teach you a lot of theory about using natural keys, algorithms to sort structures, avoid
      data replication, and so on. Not always these approaches will drive you to a manageable software: learn to deal
      with surrogate keys, duplicate data when it makes sense (e.g., network latency, historical reasons and so on).
    • learn the tools: developers around the world need to coordinate. Learn to use bug reports, stay on mailing lists, IRC channels,
      and alike. Don't ask stackoverflow for someone to do your homework, learn how to find documentation and search for answers. Become
      acquainted with revision control, compilers, linkers, and different platforms.
    • document for someone else: it is easy for you to explain what you have done to your teacher, in particular if you did it in the very
      previous period of time (typically a semester). But can you document something so that another developer, even another student like
      you, can understand one year later why and how you did a task?
  3. do not start a project from scratch: typically the university cycle during semesters is something like design-implement-compile-run-explain-throw away_
    and then you start over and over again every time you got an assignment, homework, or project. This is wrong! Real life does not work as such:
    in real life you are often assigned to maintain or refactor an existing piece of code without having to throw it away.
  4. learn idioms: developers around the globe are smarter than you. It is not they are more intelligent, it is just they are more expert
    and know better the subject. Reading someone else (smarter) code is a great way to improve your knowledge and become smarter too. Learning idioms,
    seeing patterns applied to real world code is a great start to become a professional coder.
  5. fun: developers have their habits. They meet together in so called conferences, and usually got beers while talking about code, travel
    around the world, and have a lot of fun. And even if you stay closed in your room, doing your homework, having a chat, a video call
    or an email in your in-box with a "thank you" or "good job!" is really fun.

There are other reasons, but the above are my main choices to push students to the open source world.
So far, it seems that you are going to have only advantages and no drawbacks, but that's not true.
Becoming an open source contributor you are going to become smarter than your own university teacher, and this is a drawback so far as
the teacher signs your curriculum. It is something that is really hard for a teacher to keep in mind, but it is so true.
I was always saying to my student, in the very beginning of each class, that at the end they will know better than me the subject, and the reason
for that is that "I'm not going to spend whole nights studying before the exam!".
So, if you are a teacher, simply accept that.
Accept that a student could prove a better algorithm, or an idiom you don't know that works. Ask him for the details, learn from him.
Learning is not a one-way process, with a god-like teacher and an idiot-like student; learning is a cooperation where someone expert provides the sparkle to someone else.
Would not be nice to see if both can grow during the path?

There is also another drawback: open source is not something you can teach without knowledge. You have to know the tools: revision control, IDEs,
bug tracking, issue tracking, wiki, testing and alike.
Most teachers urge teaching C pointers arhitmetic instead of basic tools, and that's not good.
Allow me to provide my very own example: I spent five years in a computer science degree, and nobody told me about revision control. While
doing my master thesis, being afraid of loosing some change or to mistakenly do a single line change (that will not blow up your project, right?),
I did my very own backup shell script that was running every hour to keep an historical copy of my work.
Shame on me, in the very same time I could have learnt rc or cvs (no, it was before git).

So my advice for students is be a part of an open source community, you will surely learn something that will make the difference in
your real job.
And my advice for teachers is accept smarter students and promote the adoption of an open source code base. There are a lot of "mini-hackers"
initiatives around the world (CPAN Pull Request, Hacktoberfest, etc.), pick one and try let your student do the rest.
You'll be happier, your student will be happier, the open source community will be happier and, who knows, your employer could also
become a partner in an open source community.

Considerazioni sul planet italiano di PostgreSQL

Ho fatto caso che ormai sul planet ufficiale dell'associazione ITPUG, ovvero, sto scrivendo
sporadicamente solo io. Questo secondo me è un campanello di allarme: io non sono certo migliore o piu' bravo di altri
soci e componenti dell'associazione, ma questa assenza dell'associazione dal planet indica che forse non si crede piu'
in questo strumento. Il fatto però è che nemmeno sul planet ufficiale si leggono post di ITPUG, e quindi non è tanto
la piattaforma italiana ad essere trascurata, ma il sistema di pubblicazione di notizie e articoli in generale.

Tornando al planet italiano, è facile verificare che su un periodo abbastanza ampio gli unici post
aggregati sono miei, e spesso risultano a loro volta dei link ad altre notizie ed articoli:

  1. 6 Gennaio 2017
  2. 13 Gennaio 2017
  3. 18 Gennaio 2017
  4. 27 Gennaio 2017
  5. 3 Febbraio 2017
  6. 21 Febbraio 2017
  7. 23 Febbraio 2017
  8. 24 Marzo 2017 (a) e (b)
  9. 2 Aprile 2017
  10. 5 Aprile 2017 (a) e (b)
  11. 8 Aprile 2017

Possibile che in un periodo di circa 3 mesi ITPUG non abbia avuto nessuna notizia da pubblicare?
Perfino in questi giorni, dove si richiede la regolarizzazione della quota per l'anno 2017 in vista
dell'imminente assemblea ordinaria, non vi sono notizie a riguardo.

Il consiglio che mi sento di dare al futuro consiglio è quello di prendere una decisione in merito al planet: se non lo si
vuole aggiornare allora tanto vale "spegnerlo", ma se lo si mantiene (come è tradizione anche nell'ambiente PostgreSQL e non solo)
allora lo si deve popolare periodicamente.

domenica 9 aprile 2017

Rust, Gnome & friends

C'è un certo momentum attorno al linguaggio di programmazione Rust.
Premetto che non ho mai utilizzato Rust nemmeno per il classico "Hello World", tuttavia ho provato a leggere la documentazione
e devo essere sincero: mi pare un linguaggio abbastanza complesso e pieno di keyword e di trabocchetti che non sono propri
di un linguaggio che si propone come un C migliorato.

Ad ogni modo pare che il team Gnome voglia passare a Rust per l'implementazione delle librerie e della infrastruttura ad oggetti
del famoso desktop. O meglio, si vuole pluggare componenti Rust nel sistema ad oggetti GTK+. Conosco GTK+, anche se sono molti
anni che non lo "utilizzo" da vicino: l'idea è bella ed elegante e si basa sulla costruzione di un sistema OOP puramente in C.
E riesce bene nello scopo, a patto di ricordarsi alcune regole boilerplate per la definizione dei nuovi tipi (classi).
Evidentemente, proponendosi Rust come un super-C ad oggetti, l'idea del team Gnome è quella di usare Rust per ridurre il codice
boilerplate pur mantenendo la dorsale GTK+ (per ovvia retrocompatibilità).

Francamente non mi pare che Gnome negli ultimi anni si sia mosso nelle giuste direzioni, e questa di Rust mi sembra l'ennesima
"cantonata" dettata quasi piu' dalla moda che non da problematiche tecnologiche. GTK+ è un sistema solido, testato, affermato
e modificarlo adesso creando una sorta di super-binding per Rust rischia di confondere i programmatori e creare piu' incompatibilità
rispetto ai problemi risolti. Sono sicuro che Gnome ha valutato molto bene vantaggi e svantaggi di questo approccio, e la mia resta
una pura riflessione personale, ma onestamente trovo abbastanza complesso e poco elegante il linguaggio scelto.

Da ultimo, per una visione piu' simile alla mia, si legga questo interessante articolo sull'adozione di nuovi linguaggi (fra i quali Rust).

sabato 8 aprile 2017

PostgreSQL ltree

Da un thread su una delle mailing list ufficiali ho appreso di un tipo di dato a me sconosciuto: ltree.
Un ltree rappresenta un label tree, quello che nei linguaggi di programmazione è un meccanismo di properties. In sostanza
si definiscono delle etichette, ordinate gerarchicamente in base ad un separatore (il carattere .) e a queste si associa un valore.
Esistono poi funzioni apposite di utilità per la navigazione e la ricerca nell'albero delle etichette.

Vediamo un esempio pratico: supponiamo di voler catalogare in un albero alcune informazioni basilari riguardo la nostra associazioni
(dati assolutamente casuali e a puro scopo didattico!).

itpug( tipologia ltree, counter integer );

Ora si supponga di voler trovare il dettaglio dei soci, magari la loro somma partendo dalla foglia dell'albero:

SELECT sum( counter )  
FROM itpug  
WHERE tipologia @> 'itpug.soci';
(1 row)

Supponiamo di voler trovare tutte le informazioni relativi al web (si noti l'uso della tilde):

FROM itpug  
WHERE tipologia ~ '*.web.*';
         tipologia         | counter
 itpug.web.siti            |       3
 itpug.web.ssl.certificati |       1
(2 rows)

Insomma, ltree si presenta come estensione sicuramente interessante, anche se forse un po' sorpassata dall'uso di altri formati quali json e jsonb.

venerdì 7 aprile 2017

Emacs yank at point (dal mouse)

Finalmente, dopo tanto tempo, ho trovato un trucchetto per far si che
Emacs incolli la clipboard tramite mouse dove c'è il cursore.
Mi spiego meglio: quando in Emacs si effettua un yank (incolla) dal mouse, mediante il pulsante centrale,
la parte copiata viene posizionata dove si trova il puntatore del mouse nel buffer, spostando il cursore in quel
punto (point). La cosa può risultare noiosa, perché spesso si vuole copiare un comando o un link da altre applicazioni
senza spostare il cursorse e senza dover posizionare esattamente il puntatore del mouse.
Niente di piu' semplice nell'editor piu' configurabile del mondo:

(setq mouse-yank-at-point t)

Valorizzando la variabile mouse-yank-at-point si ottiene il comportamento desiderato: il mouse incolla (yank)
dove si trova il cursore (at-point) senza spostare quest'ultimo.

giovedì 6 aprile 2017

Eclipse Survey

C'è una survey "What is Eclipse?" disponibile per la compilazione anonima.
Il questionario, composto da 16 domande, non è basato su aspetti tecnologici spinti, quanto sull'utilizzo stesso dei progetti
delle tecnologie alla base di Eclipse.
Penso valga la pena spendere qualche minuto per la compilazione.

mercoledì 5 aprile 2017

Planet PostGIS

Scopeto da poco e quasi per caso: il è il planet ufficiale dell'estensione spaziale di PostgreSQL. La sua funzione è simile a quella del planet ufficiale di PostgreSQL: aggregare notizie ed esperienze da blogger di tutto il mondo relativamente al solo ambito GIS.
Non è sicuramente uno dei planet che leggerò quotidianamente, ma vale la pena tenerlo presente per avere un'idea dell'evoluzione del mondo GIS legato all'ecosistema PostgreSQL.

if-else in psql

Un articolo interessante che punta ad un commit di Tom Lane (e altri) per l'introduzione in psql di un costrutto if-else, ovvero \if, \elif, \else.
Mi torna subito alla mente Larry Wall in Programming Perl: soltanto un Algol-er potrebbe usare una parola chiave che è file scritto al contrario.
Ad ogni modo la modifica, che verrà introdotta con PostgreSQL 10, permette l'uso di costrutti condizionali basilari in psql. Si presti attenzione
che questi non sostituiscono i costrutti condizionali di plpgsql, ma si inseriscono direttamente nell'interprete SQL base fornito a corredo di
praticamente ogni installazione.
Sarà quindi possibile realizzare script automatici piu' complessi da dare in pasto al nostro fidato amico psql.

domenica 2 aprile 2017

Assegnamento e binding (in Perl)

Un articolo molto interessante e ben fruibile sulla definizione di variabili (e chiusure) e del loro livello "intermedio".
Effettivamente non mi ero mai posto il problema della differenza fra assegnamento e binding a livello di run-time.

Pipeline di una query in PostgreSQL

Una interessante spiegazione da parte di Tom Lane sulla pipeline di una query.
Anche se si fa riferimento all'utilizzo, non ammesso dallo standard SQL, degli alias SELECT in una clausola WHERE, la mail spiega in modo molto dettagliato come devono essere valutati
i vari passi della sintassi di una query.

Essere green...

Da alcuni anni va di moda essere green: "non stampare questa email, rispetta l'ambiente", "non stampare lo scontrino, scelta piu' ecologica!".
Sarebbe interessante approfondire se veramente queste scelte sono green come si vuole indurre il povero stolto utente a pensare.
Prendiamo il caso dei bancomat, che recitano spesso la dicitura "scelta piu' ecologica" a fianco al pulsante per evitare la stampa dello scontrino
dell'operazione. Sarei uno sciocco nell'affermare che la stampa dello scontrino è un'operazione piu' ecologica che la "non-stampa" dello stesso.
Ma usiamo il cervello per una volta, invece che fidarci di cosa c'è scritto sul monitor dell'ATM di turno.
Sullo scontrino dell'operazione viene riportato il saldo, giusto per fare un esempio. Se io ho bisogno di sapere il saldo come posso fare se non ho la stampa
dello scontrino?
Semplice: devo collegarmi all'home banking e vederlo da lì.
Ma collegarsi all'homebanking è green?
Apparentemente si, ma il consumo di energia elettrica (nonché le onde elettromagnetiche) valgono
il risparmio della non-stampa dello scontrino? Secondo me NO!

Ovvio, la scelta green è quella di non stampare lo scontrino, qualora non se ne abbia reale necessità, o di stamparlo e conservarlo il tempo necessario.

Bene, ma c'è forse una ovvietà che sfugge (o si vuole far sfuggire) nella "scelta piu' ecologica": quella è anche la scelta piu' economica per la banca stessa
che risparmia infatti inchiostro, carta e usura della stampante collegata all'ATM.

Quindi, cara banca, se il tuo vero scopo è quello di promuovere un comportamento green perché non istituisci un premio, ad esempio 10 centesimi
di accredito ogni 20 operazioni ATM senza la stampa dello scontrino?

Caso ancora peggiore quello che riguarda le email, che spesso recitano in calce frasi chilometriche sulla necessità di riflettere circa la stampa
della lettera stessa. Nella mia breve carriera di informatico devo ancora conoscere la persona che stampa una email prima di leggerla, ma ammettiamo che
tali persone esistano e quindi tutto il genere umano debba essere avvisato dell'immane pericolo di costoro.
Dicevo: frasi chilometriche, che non solo distolgono dalla lettura, ma costano per la trasmissione di un messaggio così allungato di alcune righe.
Si, costano, perché spedire poche righe non è equivalente, in termini sempre elettrici (potenza di calcolo dell'infrastruttura di rete), a spedirne
una quantità aumentata. E anche se la quantità aumenta di poco, si pensi a quante volte questa quantità viene spedita a differenti destinatari.
Siamo sicuri sia una scelta green?

La realtà, come in tutte le cose, è che essere green non significa ripudiare la carta, significa usare le proprie risorse
con intelligenza e ragione.
E anche il fatto di non rompere continuamente le scatole agli altri cercando di imporgli comportamenti di massa potrebbe essere
un passo avanti per essere green.

mercoledì 29 marzo 2017

Proprietà anticommutativa

Ovvero invertendo l'ordine delle sostanze il gusto cambia.

Ah già, non dovrei scrivere di cose troppo personali...
Inizia un nuovo periodo di prova, con una linea discendente e un terzo quadrante velato, perché il terzo quandrante è quello di (-x, -y).
E sia chiaro che "meno per meno" non fa piu', quella è una fantasia dei matematici che poco ha a che fare con la realtà.
Qui "meno per meno" fa "molto meno", che la moltiplicazione dei pani e dei pesci la lascio fare ad altri (senza voler essere blasfemo, magari un po' disilluso).

Mi veniva anche un po' da sorridere sentendo "la sfiga ci vede benissimo".
Senti, cara sfiga, micca possiamo fare a metà?

Vabbé, resto fermo aggrappato ai miei valori.

venerdì 24 marzo 2017

Caratteri, codifiche, ordinamento

Un articolo molto chiaro e sintetico che aiuta a tenere a mente i concetti dietro collation, charater encoding e charset.
Consiglio vivamente la lettura.

Chi non paga il biglietto su gigetto?

Vado e non evdao!
QUesto è lo slogan delle ferrovie regionali che si applica anche, a suon di cartelloni illustrati stile for-dummies, al caro gigetto, spina dorsale metallica Modena-Sassuolo.

C'è qualcuno che realmente non paga il biglietto su gigetto?

Impossibile! Già, direi impossibile se proprio qualche settimana fa mi sono trovato davanti tre controllori, con tanto di divisa e felpa che recava sul retro una dicitura (manco fossero una squadra agonistica). Beh, tre controllori su un treno a due vagoni direi che è un buon esempio di ridondanza...impossibile quindi sfuggire ai controlli. 
 Le risorse regionali vengono quindi ben sfruttate nella lotta alla criminilità!

Eppure, a parte l'episodio sopra citato che non ha visto, nella mia esperienza, un seguito, devo riportare qualcosa di molto piu' controverso.
Capita sovente, almeno ogni viaggio, che ci siano viaggiatori onesti che vorrebbero fare il biglietto a bordo. Non importa la ragione, sia una coincidenza (ferroviaria), un errore di acquisto o di obliterazione, insomma per qualche ragionevole motivo qualcuno vuole fare il biglietto direttamente a bordo (rendendosi quindi disponibilie a pagare anche l'esoso sovrapprezzo).
Ma cosa bisogna fare per fare il biglietto a bordo?
Semplicissimo: si cerca il capotreno o il controllore e lo si informa della necessità di voler acquistare il biglietto.
Tipicamente il capotreno/controllore risponderà che arriva subito per incassare il biglietto.
A questo punto basta godersi il viaggio in attesa appunto che qualcuno venga ad emettere il biglietto. Eh si, perché partiti da Modena nessuna persona addetta si trova in giro per il treno, e quindi le eventuali richieste di biglietti cadono nel vuoto.

Ora i piu' polemici potrebbero interpretare questo come l'ennesimo spreco di risorse e soldi: ma come, i viaggiatori vogliono fare i biglietti e non gli viene concessa questa possibilità?

Ma niente polemica, in pochi forse capiscono che la regione ha messo in palio, con concorso segreto, la promozione vado, non evado e rivado: mostrando la volontà di voler fare il biglietto il viaggiatore vince un altro biglietto per la prossima corsa, anch'esso da fare sul treno stesso!

martedì 21 marzo 2017

SpeakerFight & PGDay.IT: è possibile?

Sono venuto a conoscenza per caso di un progetto interessante: SpeakerFight.
L'idea è abbastanza semplice, e l'implementazione mantiene la semplicità: si inviano dei contributi di talk (per conferenze ed eventi) e si lascia che le persone li votino, in un meccanismo stile "star" ben noto da altre piattaforme. I talk/contributi che hanno ricevuto il maggior numero di voti vengono selezionati per l'evento.

Un paio di giorni fa ho proposto di valutare questo meccasnimo nell'ambito del PGDay.IT. Da tempo sono sostenitore di una call-for-papers piu' aperta e con selezione maggiormente trasparente rispetto a quanto è avvenuto nelle ultime edizioni. Anzi, a dire il vero ho anche proposto piu' volte di fare un "speaker fight" del poveraccio addirittura per il keynote, proponendo di chiedere alla community chi fosse interessato a fare un keynote speech invece che andare su singolo invito diretto.

Ora sistemi come quello qui descritto hanno, ovviamente, i loro svantaggi: per esempio si potrebbe votare molto un talk tenuto da un perfetto incompetente che risulterebbe in uno speech di pessima qualità, trascurando magari talk meno "accattivanti" ma di sicuro successo ed impatto.
E forse alcune persone non vogliono selezionare di propria volontà i talk, quanto lasciare che siano gli organizzatori a "stupirli" con contenuti all'altezza di stimolare la curiosità e l'intelletto.
Tuttavia è difficile rimanere in un ambito o nell'altro se non si hanno dati alla mano circa il gradimento delle precedenti edizioni (questione che spesso ho sollevato).

Personalmente ritengo che aprire almeno una porzione del PGDay.IT ad un sistema di votazione diretta possa dare quella spinta ad autori e partecipanti per sentirsi maggiormente coinvolti e, soprattutto, per poter decidere il livello dei contenuti da visionare, garantendo quindi una maggiore partecipazione (almeno in teoria).
Se poi il tutto viene accompagnato anche da un feedback sui talk, si può avere una base dati abbastanza oggettiva che possa permettere un evento migliore ad ogni edizione.

Per sessioni interattive penso che un sistema di gradimento anticipato sia fondamentale: organizzare una sessione interattiva (es. ITPUG-Lab) non è semplice e rischia di portare via risorse (sia organizzatori che partecipanti) dalle track della conferenza. Occorre quindi essere certi del gradimento e della partecipazione alla sessione.

Insomma, una sfida interessante e uno spunto per i prossimi organizzatori dell'evento perché si possa sempre migliorare e non "sedimentare" su formule sempre uguali e ripetute.

domenica 19 marzo 2017

DBIx (DBIC) in 10 minuti

Per chi proviene dal mondo "classico" della programmazione ORM (Object Relational Mapping), il componente DBIX può risultare un po' ostico. Il problema principale, a mio avviso, è nella terminologia e nella modalità di utilizzo. Se un ORM classico infatti richiede solitamente un livello intermedio di persistenza (tipicamente tramite una sessione, dao, ecc), DBIX tende a fornire oggetti (proxy-zzati) molto piu' autonomi.
In questo brevissimo articolo voglio mostrare come può essere usato DBIX agilmente nelle operazioni piu' comuni.

1 Creazione del database

Si supponga di creare uno schema SQLite3 molto semplice:
sqlite3 database.db
sqlite> CREATE TABLE events( pk integer primary key autoincrement,
ts timestamp default current_timestamp,
description varchar(200) );

2 Eseguire il dump dello schema

Il programma dbicdump permette il dump di uno schema database, ovvero la creazione degli oggetti result che rappresentano ciascuno una tabella singola del database/schema.
 dbicdump -o dump_directory=./lib -o components='["InflateColumn::DateTime"]' \
Dumping manual schema for Test::Schema to directory ./lib ...
Schema dump completed.
Dopo aver eseguito questo comando si avrà una directory lib che contiene un file (che dichiara il namespace) e il relativo albero di directory Schema/Result con un file pm per ogni DBIX result, ovvero un file per tabella. In particolare, per la singola tabella event rappresentata dalla classe nel file si ha che:
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
    data_type     => "timestamp",
    default_value => \"current_timestamp",
    is_nullable   => 1,
  { data_type => "varchar", is_nullable => 1, size => 200 },
Si nota come il sistema di gestione dei result sia di tipo dichiarativo: ogni classe (Perl) indica in modo dichiarativo i propri membri, e ciascun membro corrisponde sostanzialmente ad una colonna del database.
L'utilizzo di DBIX risulta veramente veloce e semplice (dal punto di vista della quantità del codice). La classe schema eredita da DBIX::Schema e quindi dispone del metodo connect che consente la connessione al database. Ogni azione che si vuole fare su un database (istanza) deve avvenire attraverso lo schema. Lo schema raccoglie una serie di tuple raggruppate in un result set (inteso qui differentemente rispetto a JDBC, DBI, ecc). Ogni operazione che si vuole svolgere su una tupla deve passare attraverso un result set. In sostanza si potrebbe affermare che l'analogia fra DBI e DBIX è la seguente:
DBI - JDBC - similari DBIX Significato
database handler schema Connessione al database (istanza)
result set array di oggetti Risultati da una query
statement su tabella result set Interazione lettura/scrittura con una tabella

3 Un primo esempio

Ecco quindi un programma di esempio:
use Test::Schema;

my $schema_connection = Test::Schema->connect( 'dbi:SQLite:/home/luca/tmp/dbix/database.db' );

for ( 1..100 ){
    my $event = $schema_connection
 ->resultset( 'Event' )
 ->create( { description => "Evento $_" } );

    # update !

# ricerca di eventi
my $result_set = $schema_connection->resultset( 'Event' )->search( { pk =>{ '>' => 50, '<' => 87 }  } );

while ( ( my $event = $result_set->next() ) ){
    say "Evento " . $event->pk . " = " . $event->description;

La prima parte carica lo schema e gli oggetti creati appositamente da ~dbicdump~.
Successivamente si crea lo schema, ovvero ci si collega al database (istanza).
Poi si effettua un semplice ciclo per inserire 100 righe nella tabella /events/, corrispondente all'oggetto Perl ~Event~.
Si noti che tali oggetti sono creati con il metodo ~create~ sul result set, che a sua volta è agganciato alla tabella /events/.
In altre parole la riga

#+negin_src perl
my $event = $schema_connection
 ->resultset( 'Event' )
 ->create( { description => "Evento $_" } );
  1. una connessione al database (istanza) usando lo Schema;
  2. decide di lavorare sul result set Event, ovvero sulla tabella corrispondente events;
  3. chiedendo di creare un oggetto che rappresenta una singola riga nella tabella.
Giunti a questo punto DBIX sa tutto quello che gli serve per memorizzare in modo permanente sul database l'oggetto, e quindi tradurlo in una riga di SQL. Da notare che l'oggetto creato tramite il metodo create è un oggetto Perl a tutti gli effetti, ovvero è possibile usare i metodi accessor per impostare i valori.
Fino a qui DBIX si è solo "preparato", ma non ha eseguito nulla di fatto. Nel momento in cui l'oggetto si aggiorna, ovvero si invoca il metodo update, questo viene inserito/aggiornato nella tabella.
Questo è un punto nodale che rappresenta una differenza fondamentale nell'approccio DBIX rispetto ad altri ORM (es. Hibernate): invece che creare degli oggetti slegati dal database e richiedere ad un servizio DAO di rendere persistente tali oggetti, DBIX crea oggetti collegati alla propria tabella e al proprio database, così che tali oggetti "sappiano" dove andare ad "inserirsi" o "aggiornarsi". Nessuna cosa, entità o servizio di persistenza esterno viene invocato.
Successivamente è possibile rileggere i dati inseriti, ancora una volta passando per il result set:
  1. si usa la connessione al database (istanza), ovvero lo Schema;
  2. si dichiara di voler lavorare sullo schema Event, ovvero la tabella fisica events;
  3. si effettua una ricerca specificando, per ogni colonna che si vuole cercare, i valori da mettere in and-logico. Nell'esempio di cui sopra si ha che si prendono tutte le tuple con pk compreso fra 51 e 86 inclusi. L'oggetto ritornato è un Result Set, da intendersi come collezione di oggetti del tipo Event.
Si può allora iterare sul result set per ottenere tutti gli oggetti di tipo Event richiesti. Da notare che è possibile specificare delle clausole where letterali, il metodo search può accettare diversi formati di parametri. Inoltre esiste un metodo find per la ricerca secca di un record per chiave primaria.

4 Update massivo

Ci sono due strade per effettuare un aggiornamento massivo di record:
  1. iterare su ogni oggetto collegato ad una tupla ed eseguire un update singolo;
  2. eseguire un update sul result set trovato da una query di ricerca.
Il primo metodo potrebbe riassumersi così:
my $result_set = $schema_connection->resultset( 'Event' )->search( { pk =>{ '>' => 50, '<' => 87 }  } );

while ( ( my $event = $result_set->next() ) ){
    $event->description( " AGGIORNATO " . $event->description() );
L'idea è semplice: si effettua una ricerca, si prendono uno ad uno gli oggetti Event (ciascuno dei quali corrisponde ad una tupla) e lo si aggiorna tramite update. DBIX è sufficientemente intelligente per capire che deve eseguire una UPDATE verso il database, siccome la riga proveniva da una query.
Il secondo approccio, sicuramente piu' compatto e simile, come concetto, alla query SQL che si vorrebbe eseguire, è il seguente:
my $result_set = $schema_connection->resultset( 'Event' )->search( { pk =>{ '>' => 50, '<' => 87 }  } );
$result_set->update( { description => 'AGGIORNATO' } );
La differenza, ovvia, è che non si possono chiamare degli accessor nel caso di update di un intero result set, e quindi questa tecnica è effettivamente utile quando si devono impostare tutti i valori ad un letterale prestabilito.

5 Colonne modificate, annullare le modifiche

Un singolo oggetto collegato ad un result set è in grado di sapere quali colonne sono state modificate:
$event->description( " AGGIORNATO " . $event->description() );
    my %cols = $event->get_dirty_columns();
    for my $col ( keys %cols ){
 say "Evento " . $event->pk . " con colonna $col modificata " ;
e quindi è anche possibile evitare di eseguire delle query qualora l'oggetto non sia stato modificato:
$event->update() if ( $event->get_dirty_columns() );

6 Cancellazione

Beh, come ci si puo' aspettare, delete esegue lo scopo:

7 Insert o Update?

Inizialmente ho mentito: non è necessario chiamare update per eseguire una query INSERT o una query UPDATE. Come ci si può aspettare il metodo update esegue…eh.. UPDATE! Esiste infatti un metodo insert da richiamare su un oggetto risultato per eseguire l'inserimento nella tabella corrispondente. Il motivo per il quale nel primo esempio si è usato update è che lo schema ha creato l'oggetto con create, che inserisce di fatto una tupla nella tabella corrispondete. L'update successiva aggiorna tale tupla. E' possibile creare un oggetto senza inserirlo nel database usando il metodo new sul result set, che restituisce un oggetto sul quale occorre poi fare una insert esplicita:
my $event = $schema_connection
    ->resultset( 'Event' )
    ->new( { description => "!!Evento $_" } );


8 Aggiungere foreign keys

Supponiamo di voler dotare ogni evento di un "tag":
sqlite> CREATE TABLE tags( pk integer primary key autoincrement, description varchar(20) );
sqlite> INSERT INTO tags( description) VALUES( 'Tag1' ), ( 'Tag2' );
sqlite> DROP TABLE events;
sqlite> CREATE TABLE events( pk integer primary key autoincrement,
   ...>    ts timestamp default current_timestamp,
   ...>    description varchar(200),
   ...>    tag_pk integer,
   ...>    FOREIGN KEY(tag_pk) REFERENCES tags(pk) );
Se si ricostruisce il modello Perl con dbicdump si ha che viene creata una classe Tag che contiene nella definizione il seguente blocco:

=head2 events

Type: has_many

Related object: L


  { "foreign.tag_pk" => "" },
  { cascade_copy => 0, cascade_delete => 0 },
che istruisce DBIX sul fatto che Event e Tag sono in relazione attraverso le chiavi esterne. Analogamente Event viene modificato rispetto a prima:
  { pk => "tag_pk" },
    is_deferrable => 0,
    join_type     => "LEFT",
    on_delete     => "NO ACTION",
    on_update     => "NO ACTION",
Vediamo ora come è possibile inserire delle tuple che agiscano su entrambe le tabelle:
use Test::Schema;

my $schema_connection = Test::Schema->connect( 'dbi:SQLite:/home/luca/tmp/dbix/database.db' );

for ( 1..100 ){
    my $tag = $schema_connection
 ->resultset( 'Tag' )
 ->find( ( $_ % 2 ) + 1 );

    my $event = $schema_connection
 ->resultset( 'Event' )
 ->new( { description => "Evento $_" } );

    $event->tag_pk( $tag );
Come si può notare si seleziona in modo "random" un tag dal resultset Tag, e poi lo si imposta nell'oggetto Event che viene poi inserito tramite insert. Da notare una bruttura: DBIX chiama il metodo accessor della relazione come la colonna stessa della tabella, in questo caso tag_pk. Ovviamente non è DBIX, quanto dbicdump, quindi questa cosa va tenuta presente e/o modificata a mano per rendere piu' leggibile il codice Perl (dopotutto tag_pk() agisce su un oggetto Tag e non su un oggetto integer). Se ad esempio si modifica come segue:
  "tag",  # era tag_pk
  { pk => "tag_pk" },
    is_deferrable => 0,
    join_type     => "LEFT",
    on_delete     => "NO ACTION",
    on_update     => "NO ACTION",
allora il seguente codice usa il metodo mnemonico tag che ricorda meglio l'oggetto risultato Tag:
for ( 1..100 ){
    my $tag = $schema_connection
 ->resultset( 'Tag' )
 ->find( ( $_ % 2 ) + 1 );

    my $event = $schema_connection
 ->resultset( 'Event' )
 ->new( { description => "Evento $_" } );

    $event->tag( $tag ); # era tag_pk
A questo punto si può procedere in maniera analoga alla ricerca dei dati, il metodo Event::tag fornirà un oggetto di tipo Tag mentre Tag::events fornirà una lista di oggetti Event. Ad esempio:
my $result_set = $schema_connection->resultset( 'Tag' )->find( 1 );

for my $event ( $result_set->events() ){
    say "Evento trovato " . $event->description;
da notare che sul result set non si deve effettuare nessuna operazione poiché il metodo find restituisce un solo valore Tag.

9 Riassunto

DBIX è un meccanismo molto flessibile e potente, che opera a livello semi-dichiarativo: come si è potuto notare ogni oggetto collegato ad una tupla database è espresso tramite invocazioni di metodi che ne definiscono struttura e relazioni (es. belongs_to). Il concetto chiave di DBIX è, a mio avviso, il fatto che il result set non rappresenta un set di risultati quanto una tabella fisica (o un join di tabelle), e che di conseguenza ogni singola tupla di tabella/e diventa un oggetto capace di agire autonomamente sulla propria persistenza. Ne consegue che ogni volta che si vuole interagire con una tabella si deve passare per un result set, sia per aggiungere, aggiornare, cancellare o selezionare tuple, che a loro volta diventano oggetti, che includono la logica dao al proprio interno.

sabato 18 marzo 2017

Stai attento ai tuoi soldi, non a quello che firmi!

C'è da un po' di tempo una pubblicità televisiva che mi ha lasciato molto amareggiato per il livello di stupidità planetaria raggiunta.
L'idea è quella di far notare, come che ce ne fosse bisogno, che i soldi non vanno sprecati e che stare attenti alle proprie spese è la prima mossa per
non rimetterci in denaro (e forse anche in salute).
Evidentemente chi ha ideato questa pubblicità non è mai stato mandato da piccolo a comprare il giornale o il pane; se ci fosse andato avrebbe ricevuto le
giuste raccomandazioni sul resto da riportare a casa.

Ad ogni modo la raccomandazione dello spot pubblicitario è giusto, anche se il fatto che uno spot commerciale sia mirato ad un risparmio mi fa sorridere.
E quindi, nell'evoluzione mentale che porta tutti a dover stare attenti ai propri soldi, possibile che esistano dei professionisti che
chiedono di firmare contratti errati, sapendo e ammettendo che lo sono?
Già, la fiducia galoppa, un po' come in altri spot dove si esortano i bambini a lasciare le porte di casa aperte…

Ed è proprio al grido di "fidiamoci" che si spingono le persone a firmare contratti densi di penali, mal scritti e peggio composti, e il cui scopo finale è sempre solo
il trattamento economico. Già, "fidatevi", che poi cambiamo a penna importi e condizioni, ma dopo che avete firmato.
Uhm…perché non prima?
Ah beh, non cambia, si potrebbe farlo anche prima, si potrebbe anche fare un contratto nuovo con le giuste cose al giusto posto.
E allora perché non lo si fa?
Ah già, fidiamoci…

E che piovano fuoco e fiamme sui poveri malcapitati dotati ancora di intelletto e che non sono disposti ad accettare un simile
atteggiamento "professionale".

Io sono disposto a fidarmi, ma solo di gente che mi mostra con i fatti che la loro parola vale, che la loro parola conta.
Di tutti questi "professionisti" non posso avere stima, compassione, e non lascio nemmeno che instaurino il dubbio nella mia mente.
Ma ritengo che ormai siamo destinati ad una follia di massa che ci spingerà tutti verso il basso.

Caino sta vincendo a mani basse.

mercoledì 15 marzo 2017

Lexpad, chiusure e variabili

Un articolo molto interessante e ben fruibile sulla definizione di variabili (e chiusure) e del loro livello "intermedio".
Effettivamente non mi ero mai posto il problema della differenza fra assegnamento e binding a livello di run-time.
Consiglio vivamente la lettura.

martedì 14 marzo 2017

Scacco matto!

Pur non essendo un abile giocatore di scacchi, ritengo di saperne a sufficienza per una partita amatoriale.
Sicuramente non mi sarei mai aspettato che mio figlio, alle prime armi e di soli 6 anni, riuscisse a farmi uno scacco matto.
Sia chiaro, si tratta di una casualità, non certo di genialità nascosta, ma comunque è stata per lui una grande soddisfazione!

domenica 12 marzo 2017

Scripting si o scripting no?

Scripting o programmazione?
Dibattito fondamentalmente inutile ma che ogni tanto salta nuovamente fuori nelle mailing list, forum e discussioni tecniche.
Fornisco la mia visione a riguardo, sottolineando che non intendo cerco convincere nessuno e, allo stesso tempo, non intendo essere convinto.

Inizialmente la distinzione era forse fin troppo facile: un linguaggio è di scripting se viene interpretato invece che compilato.
Ma il termine "interpretato" può confondere: qui si intende interpretazione non anticipata.
In altre parole immaginiamo di fare uno script di due righe, prendiamo in prestito una qualche shell Unix:

echo "Hello World!"
heco "Bye bye!"

Ora la prima riga è corretta, la seconda presenta un errore di sintassi.
Se il linguaggio è interpretato on-the-fly il risultato sarà quello di eseguire la prima riga, con stampa in output della frase, e il blocco per errore di sintassi quando si tenta di interpretare la seconda riga.
Se invece il linguaggio subisce una pre-analisi (sia questa una compilazione, controllo sintattico, ecc.) si avrà che l'esecuzione intera del programma viene abortita.

Su questa base è facile dividere i linguaggi in "scripting" e "programmazione".

Con l'andare del tempo però la differenza si è molto assotigliata. Si può affermare che Java è non-interpretato? Inizialmente forse lo era (si interpretava il bytecode), magari con compilatori JIT/HotSpot non vale piu' questa distinzione.
Eppure Java non ha mai concesso l'avvio di un programma con errori di sintassi (anche se alcuni tools, tipo Eclipse, consentono ora di inserire apposite eccezioni in blocchi di codice che non passano la compilazione, ma questa è un'altra storia).

Allora si potrebbe pensare che un linguaggio di programmazione deve avere una forma compilata "stoccabile", e in questo allora Java rientra visto che dispone dei file .class. Anche altri linguaggi, come ad esempio C/C++ forniscono i loro file object.

La pace nel mondo è tornata dunque!

Ma a guastare le cose vengono poi altri linguaggi, che solitamente non dispongono di un formato compilato, e che per ottimizzazioni spinte invece forniscono ora anche delle versioni compilate e stoccabili.

Allora la distinzione dipende dal fatto che vi sia a disposizione un compilatore da usare sempre?

Uhm...forse l'approccio dovrebbe essere differente.

La mia personale visione è che lo scripting sia qualcosa che viene fatto rapidamente, senza troppi prototipi, senza ore di design e refactoring.
E per fare questo non voglio un compilatore da invocare per sentirmi dire che non ho inizializzato una variabile!
Quindi se devo fare un programma Perl che mi esegua un find su una serie di cartelle, o un programma shell che mi effettui un backup regolare di un filesystem, allora dico che ho prodotto uno "script". Indipendentemente dal linguaggio, a patto che possa modificare il programma stesso con il solo uso di un editor di testo e nessun altro tool.

Ma se il programma diventa complesso, richiede una struttura dati solida, un albero sorgenti complesso e, perché no, un packaging, allora sto producendo un "programma".

In sostanza la differenza, nella mia testa, è data solo da dimensione e complessità. Il linguaggio di programmazione è solo uno dei tanti dettagli tecnologici, anche se la sua scelta può influenzare dalla partenza la complessità stessa e quindi il risultato finale.

Non farò MAI uno script in Java: il linguaggio richiede una classe, che abbia un main, magari un jar per eseguire o chissà che altra diavoleria.
Farò a volte dei programmi Perl: dovrò costruire moduli, classi, importare librerie e chissà che altro.

Sull'argomento è molto interessante l'articolo di Larry Wall.

venerdì 10 marzo 2017

Sacchi di monete... Perl!

C'è una baggianata che gira ultimamente su WhatsApp relativamente a mesi nei quali avvengono coincidenze stranissime: 5 domeniche, 5 sabati, ecc.
L'evento viene citato come rarissimo, tanto che si verificherebbe solo 823 anni (o qualcosa di simile).
Ovviamente si tratta del solito spam mirato solo a collezionare informazioni e numeri di telefono.

Ma sarà vero?
Basta scrivere qualche riga Perl per sapere quali sono i mesi nei quali sono presenti un certo numero di ripetizioni dello stesso giorno della settimana.
Ecco quindi la mia soluzione:

use v5.20;
use DateTime;
my $now = DateTime->now();

for ( 1..823 ){
    $now->set( day => 1, month => $_ )
    say "Anno " . $now->year . " mese " . $now->month
    . " ha 5 " . join ', ', qw( Lun Mar Mer Gio Ven Sab Dom )
    [ map {  $_ , ( $_ + 1 ) % 7 , ( $_ + 2 ) % 7  } ($now->day_of_week - 1 ) ]
        for ( grep { ( $_ % 2 == 0 && $_ > 7 ) || ( $_ % 2 == 1 && $_ <= 7 ) } ( 1..12 ) );

    $now->add( years => 1 );

L'idea è semplice: anzitutto si filtrano i mesi con 31 giorni, funzione svolta dall'operatore grep che estrae i numeri di mese 1,3,5,7,8,10,12.
Questi vengono usati per impostare il mese corrente, si seleziona il primo giorno e si mappa il day_of_week in una lista di tre valori: la posizione del primo
giorno della settimana e dei due successivi adiacenti (modulo 7, ovviamente per consentire il "giro"). Il tutto viene poi stampato.
Il trucco è che il ciclo è postfisso, quindi va letto da destra a sinistra; inoltre l'inizializzazione del mese corrente è in "and" logico alla stampa
del valore, fatto che consente di usare un solo ciclo.
Per andare avanti di un anno, beh, basta aggiungere un anno e reiterare.
L'output è semplice:

Anno 2838 mese 3 ha 5 Lun, Mar, Mer
Anno 2838 mese 5 ha 5 Sab, Dom, Lun
Anno 2838 mese 7 ha 5 Gio, Ven, Sab
Anno 2838 mese 8 ha 5 Dom, Lun, Mar

Questa soluzione si basa sul semplice fatto che tutti i mesi con 31 giorni hanno sempre 3 giorni consecutivi con ripetizione pari a 5.
Ma se non fosse noto ciò?
Beh, complicandosi la vita si può fare una soluzione general-purpose:

use v5.10;
use DateTime;

my ( $count , $count_per_month, $years, $now, $dow, $ldom, $cons )
    = ( 5, 3, 823,
        DateTime->now()->set( day => 1, month => 1 ),
        sub{ qw( Lun Mart Merc Giov Ven Sab Dom )[ $_[ 0 ] ] },
        sub{ $_[ 0 ]->month != $_[ 0 ]->clone->add( days => 1 )->month },
        sub{ $_[ 0 ] == $_[ 1 ] - 1 && $_[ 1 ] == $_[ 2 ] - 1 } ) ;

for ( 1..( 365 * $years ) ) {
    my $me = \%{ $days_of->{ $now->year }->{ $now->month } };
    ++ $me->{ $now->day_of_week - 1 };

    say "Anno " . $now->year . " mese " . $now->month . " => "
        . join ', ',
        map { $me->{ $_ } . " " . $dow->( $_ )  }
          sort keys %$me
        if ( $ldom->( $now )
             && ( grep { $_ >= $count } values %$me )
             >= $count_per_month
             && $cons->( ( grep { $me->{ $_ } >= $count }  sort keys %$me )  ) );

    $now->add( days => 1 );

Il funzionamento è abbastanza semplice, anche se un po' compatto.
Anzitutto mi affido a DateTime che fornisce una interfaccia ad oggetti per la gestione delle date.
Si inizializzano una serie di variabili:

  • $count rappresenta quante ripetizioni dello stesso giorno della settimana sto cercando (5);
  • $countpermonth rappresenta quante ripetizioni di $count ci devono essere in un mese. Ad esempio impostandolo al valore 3 significa
    cercare almeno 3 giorni in un mese che si ripetano 5 volte (ossia il mese deve avere, ad esempio, almeno 5 sabati, 5 domeniche e 5 lunedì);
  • $years su quanti anni da quello corrente si vuole iterare;
  • $now la data corrente, impostata all'inizio dell'anno corrente;
  • $dow è una routine di comodo che ritorna il nome simbolico del giorno della settimana (day of week);
  • $ldom altra funzione di utilità che controlla se l'oggetto DateTime corrente rappresenta l'ultimo giorno della settimana. In particolare si controlla
    se un DateTime successivo di un giorno ha un mese differente;
  • $cons è una funzione che controlla che gli indici siano consecutivi fra loro (es 1,2,3), anche se ci vorrebbe la logica modulo 7 per
    intercettare il giro di boa alla domenica…

Da qui in poi si itera in un ciclo di 365 iterazioni per anno, salvando in un hash a piu' livelli il conteggio dei giorni della settimana di un mese.
L'hash assume una struttura dove al primo livello si trova l'anno corrente, poi il mese, poi il giorno della settimana, poi il conteggio di tale giorno.
Ad esempio una ipotetica iterazione potrebbe produrre:

 2017 => 1 => ( Lun => 3,
                Mart => 3,
                Mer  => 4,
                Gio  => 5,
                Ven => 3
                Sab => 3,
                Dom => 3
La variabile $me serve solo a tenere il codice un po' piu' compatto, ossia ad accedere alla parte dell'anno/mese corrente
nel'hash globale.
Anzitutto si incrementa il contatore del giorno della settimana (già tradotto da numerico a mnemonico con $dow).
Segue un blocco say..if, quindi è opportuno partire prima dalla condizione if: se il giorno è di fine mese e si ha un conteggio di valori di singoli
giorni della settimana superiore a quello cercato si procede alla stampa. Questo consente di valutare se stampare il mese corrente solo se si è
iterato su tutti i giorni del mese e solo se si sono trovate almeni $count_per_month ripetizioni di giorni con conteggio di $count.
La parte di say include un map preso un prestito da un idioma ben noto, quindi va letto da destra a sinistra:

  1. si ordinano le chiavi (nomi mnemonici dei giorni della settimana) dell'hash del mese corrente;
  2. si crea una stringa con il conteggio di ripetizioni del giorno specificato e del suo nome mnemonico (parte map);
  3. si effettua un join di tutte le stringhe così costruite.

E quindi si stampa il tutto, con risultato analogo a quello del caso precedente.