Visualizzazione post con etichetta programmazione. Mostra tutti i post
Visualizzazione post con etichetta programmazione. Mostra tutti i post

sabato 20 maggio 2017

Noisli: un servizio di suoni per aumentare la concentrazione al lavoro

E' molto importante rimanere concentrati durante lo svolgimento del proprio lavoro, penso sia un concetto noto a tutti.
Io ho la fortuna di svolgere un lavoro ad una scrivania, senza pubblico, quindi per estraniarmi dall'ambiente circostante utilizzo
la musica e, raramente, la radio. Il mio cervello aggancia la melodia e non si fa distrarre da altre cose e rumori, lasciandomi
quindi proseguire nel mio lavoro. Tuttavia non indico questa come possibile strategia di studio, che invece richiede maggior silenzio
e in generale sonorità che il cervello non è in grado di "comprendere" razionalmente (es. parole).

Ecco, ho scoperto che esiste un servizio Noisli che consente di comporre un suono ambientale che possa aumentare la produttività
individuale. Ad esempio mescolando il suono della pioggia battente con quello di un caminetto. E c'è anche chi ha pensato
di integrare il servizio dentro a Gnome.

martedì 9 maggio 2017

L'importanza della descrizione dei ticket...

La corretta gestione dei ticket, si sa, è una questione difficile. O meglio, non è certo la difficoltà di compilare
qualche campo in una form web, bensì è lo shift mentale che vi è richiesto per organizzare
bene i singoli ticket e le varie attività.


Un errore che vedo fare molto spesso, anche da persone che si definiscono dei professionisti informatici è quello
di usare la descrizione del ticket come il testo di una email. Ecco un esempio inventato ma non molto lontano
da diversi ticket che vedo regolarmente:


Ciao Mr.X,
avrei bisogno che mi controllassi il problema di stampa dei pdf prodotti in automatico, che hanno
l'orientamento orizzontale e non verticale. Questo non è comunque urgente, piuttosto è molto
urgente risolvere il problema di autenticazione del nuovo collega G., che non riesce
ad accedere al sistema.

Saluti,
Mr. Y

Quanti errori ci sono nella descrizione del suddetto ticket?


Anzitutto il tono "discorsivo" e quasi formale con saluti di apertura e chiusura, cosa corretta in una email, ma non
in un ticket (che ha già un owner (Mr. Y) e un assignee (Mr. X). Tali dettagli fanno solo perdere tempo e spazio,
e non sono assolutamente rilevanti per la risoluzione del ticket stesso.


Il secondo errore è la specificazione, verbale, della priorità dell'attività: […]non è comunque _urgente[…].
Tutti i sistemi di ticketing includono la possibilità di specificare la priorità delle singole attività collegate
al ticket, e il modo corretto di farlo è ovviamento usando tali campi. Perché? Beh, semplicemente perché chi prende in
mano il vostro ticket lo potrebbe fare basandosi su un filtro per priorità, dunque se il vostro ticket risulta a priorità
"normale", il fatto che voi inseriate la dicitura "urgente" non farà balzare il ticket nei primi posti della lista.


Infine, errore ancora piu' subdolo, nella descrizione di cui sopra si specificano due attività distinte (con due distinti
gradi di priorità):

  1. risolvere il problema dell'orientamento dei PDF, priorità bassa;
  2. risolvere il problema di autenticazione di un singolo utente, priorità alta.

La modalità corretta è quindi quella di aprire due ticket differenti, con priorità differente e descrizione limitata
al problema specifico.
Ecco cosa avrei prodotto io:


Ticket 1
Priorità: bassa

Problema layout stampa PDF: attualmente tutti i PDF escono con orientamento orizzontale,
mentre l'orientamento deve essere VERTICALE.

Ticket 2
Priorità: Alta

L'utente G. (username=g) non riesce ad autenticarsi. Il messaggio di errore
che riceve è "username o password errati".
Questo è per lui bloccante, non può collegarsi al sistema.


Come si può notare, oltre a "spaccare" un singolo messaggio in due, si usano livelli di priorità differente
e si specifica in maniera dettagliata ma senza fronzoli (saluti, ecc.) i problemi e i messaggi di errore.


Usare un sistema di ticketing può risultare in un meccanismo particolarmente potente e strutturato per la gestione
delle attività, ma occorre una impostazione mentale adeguata e rigorosa.
Uno dei consigli che mi sento di dare a chi si vuole "formare" e preparare adeguatamente a questo approccio è quello
di usare un sistema di ticketing anche per progetti mono-sviluppatore. In questo sistemi integrati come
Fossil consentono di unire agilmente la gestione del codice e dei ticket.

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.
Period!
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.
Why?
  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.

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).

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"]' \
  Test::Schema
  dbi:SQLite:database.db
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 Schema.pm (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 Event.pm si ha che:
__PACKAGE__->add_columns(
  "pk",
  { data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
  "ts",
  {
    data_type     => "timestamp",
    default_value => \"current_timestamp",
    is_nullable   => 1,
  },
  "description",
  { 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 !
    $event->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;
}
#+end-perl

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 $_" } );
esegue:
  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() );
    $event->update();
}
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:
$event->delete;

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 $_" } );

$event->insert;

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:
=head1 RELATIONS

=head2 events

Type: has_many

Related object: L

=cut

__PACKAGE__->has_many(
  "events",
  "Test::Schema::Result::Event",
  { "foreign.tag_pk" => "self.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:
__PACKAGE__->belongs_to(
  "tag_pk",
  "Test::Schema::Result::Tag",
  { 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 );
    $event->insert;
}
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 Event.pm come segue:
__PACKAGE__->belongs_to(
  "tag",  # era tag_pk
  "Test::Schema::Result::Tag",
  { 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
    $event->insert;
}
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.

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.

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ì 3 marzo 2017

Fluent Interfaces e method-chaining

La prima volta che ho visto una "fluent" interface è stato quando tanti anni fa ho studiato Java NIO.
Oggi la fluent interface è una buona prassi di molti linguaggi di programmazione, e in modo un po' ridicolo qualcosa di non accettato in Java stesso.
L'idea di una fluent interface è abbastanza semplice: i mutators dovrebbero ritornare un riferimento all'oggetto che modificano, così da abilitare una method chain, in sostanza:

MyObject foo = ...;
foo.setTitle( 'My Object' ).setVisible( true ).setDescription( 'Here the description' );

che sicuramente risulta piu' compatto rispetto al classico

MyObject foo = ...;
foo.setTitle( 'My Object' );
foo.setVisible( true );
foo.setDescription( 'Here the description' );

Ora, che si tratti di una versione piu' leggibile o meno è una questione di gusti, sicuramente avere linee troppo lunghe non aumenta la leggibilità, ma considerando che molti linguaggi ammettono la gestione flessibile degli spazi, una via di mezzo fra la method-chain e l'approccio classico diventa

MyObject foo = ...;
foo.setTitle( 'My Object' )
   .setVisible( true )
   .setDescription( 'Here the description' );

abbstanza compatto e leggibile, grazie ad una buona indentazione.

Ma il vero vantaggio della fluent interface si nota, a mio avviso, nell'uso combinato dei conditionals. Si consideri il seguente esempio:


MyObject foo = ...;
if ( init ) {
  foo.setTitle( 'My Object' );
  foo.setVisible( true );
  foo.setDescription( 'Here the description' );
}

che può agilmente essere riscritto eliminando la necessità di piu' istruzioni e le parentesi graffe:

MyObject foo = ...;
if ( init )
  foo.setTitle( 'My Object' );
     .setVisible( true );
     .setDescription( 'Here the description' );


E questo a me personalmente piace molto!

Ahimé i Java Beans, stupidamente, si rifiutano di accettare la fluent interface e abilitare la method-chain. Infatti la specifica impone che i mutators siano di tipo "void", ossia non ritornino nulla. E i framework aderiscono spesso severamente a questa opzione, non riconoscendo quindi i mutators che possono ritornare un riferimento all'istanza stessa.

sabato 25 febbraio 2017

PHP anonymous functions and closures: is it smart?

As many other modern lagnuages, PHP is evolving and allows the usage of so called "closures": the idea is to define a function on the fly and use it as an "handler". More than that, a closure is a special function closed on a context, in particular the context that defined itself.
This is very useful, because the function should reference variables that are going to be out of scope, in a certain way as if they have been hardcoded into the function definition itself.

PHP closures don't work really this way, and I don't get the point: a PHP anonymous function can reference instance variable (i.e., members of the class where the function is defined) but not scoped variables (e.g., a variable in the function that is defining the anonymous function). More in general, it seems to me that PHP anonymous functions can reference global variables, and this is true also for class members, but not outer scope variables.
In other words this is not going to work:

function foo( $param ){
  ...
  $closure = function( $event ){
   if ( $event == $param ) { ... }
 };
}

because the $param variable is not visible within the anonymous function.
Now, there's a solution as reported in the documentation : you can specify which variables of the outer scope the anonymous function is going to "use":

function foo( $param ){
   ...
   $closure = function( $event ) use ( $param ){
    if ( $event == $param ) { ... }
   };
}


Now PHP has the opportunity to see that $param is needed inside the anonymous function, that becomes a "real" closure.
This is just another feature I don't like in the PHP language: why is what every developer expects to be a closure not being such? Moreover the abuse of the word "use" (needed also for namespaces handling) produce an operator overload I don't like very much.
Last but not least, while I get the idea of forcing a developer to declare what he is going to use and do, why not simplifying his life declaring what is using as local (e.g., local in Perl)?

martedì 21 febbraio 2017

My, myself and PHP namespaces...

I'm starting to hate PHP with a passion.
I don't like its syntax, that I find often quite odd. The "foreach" loop is an example, I like much more the Perl or Java like idiom.
Another feature I'm trying to adapt myself to, and that incresead my hate for this language, is namespace.
The namespace definition is awkward: as first it looks like Perl's or Java's package, but it something really much orrible.
While imposing a filesystem layout (similar to Perl or Java), PHP namespaces does not provide a quick way to isolate code, or better, they isolate too much code at a time.
Let's start from the end: consider a class defined in a namespace as follows

namespace NS1\NS2\NS3;

class Foo { .. }


and now the code that should use it

use NS1\NS2\NS3;
$foo = new NS3\Foo();

Yes, you still need to partially qualify the simbol name. This is due to the fact that "use" without an alias argument is equivalent to aliasing the last part of the namespace, that is the two following lines are the same:


use NS1\NS2\NS3;
use use NS1\NS2\NS3 as NS3;

So in order to reference your symbols without any namespace prefix you have to use them all, that is:

use NS1\NS2\NS3\Foo as Foo;
$foo = new Foo();


I clearly don't get the point of using the above in a productive way: namespaces are a common way to avoid name clashing, and therefore they willc contain a set of related objects (I mean, symbols), so why would I need to use them one at a time?

Luckily (or stupidly), since PHP 7 it is possible to import a namespace the way Perl does, that is:

use NS1\NS2\NS3\{ Foo, Bar, Baz} ;

that is the same as

use NS1\NS2\NS3\Foo as Foo;
use NS1\NS2\NS3\Bar as Bar;
use NS1\NS2\NS3\Baz as Baz;

and that allows for the usage of symbol relative name (I mean, really relative name).

The syntax seems to me a crappy copy of the C++ namespace one, except that the latter has an easy way to reference relatively all symbols in an used namespace.

Now, another ugly detail of the PHP namespaces is that every class referenced within a namespace has to belong to the namespace itself or an used namespace. Why is this ugly? Because of the following:


class Logger { ... }

namespace NS1;
class Foo{
  public function __construct(){
    $this->logger = new \Logger();
    ...
  }
}


As you can see, since the Logger class does not belong to any namespace (let's say it belongs to the "main" namespace), it has to be refenced by a leading slash, that is a way to indicate the "main namespace". This is pretty much ugly because PHP could be smart enough to figure out when a symbol is found in the current and used namespaces, and if it is not it can search for it in the "\" main namespace.
Moreover, the above kindof syntax, makes it very hard to refactor existing code into namespaces; suppose you want to move the Logger class into its own namespace: you have to (i) place it into a namespace, (ii) use such namespace and (iii) remove each call to the main "\" namespace as follows

// (i)
namespace Log;

class Logger { ... }

namespace NS1;
// (ii)
use Log\{ Logger };

class Foo{
  public function __construct(){
    // (iii)
   $this->logger = new Logger();
   ...
 }
}


but step (iii) could have been removed if the original call to the Logger-in-main-namespace had been avoided. Think about Perl and its main:: package (pretty much the same as a PHP namespace), that can be omissed if not necessary:

my $x = "hello"; say $x;
$main::x = "hello"; say $x;


Last but not least: the choice of the backslash is terrible to me!
It reminds me the package separator used in Perl 4: $main'x ...
While I accept that a double colon is not optimal, due to some difficulties in editors to render it properly without any confusion with the switch C++ statement, I take it for granted it is a better established approach and makes it a lot more visible the separation.

sabato 4 febbraio 2017

Hey students, don't buy the teachers' lies!

When I was an university student I had a teacher in the Operating Systems subject that teached me (and a lot of others) to program so bad in shell scripting! I will not name her, but I had to say that today I find the very same errors around the scripts that my colleagues write every day, and this is a kind of watermark of the damage she did.

Luckily I find my way out, studying on other books and practicing on my own (Linux) computer.

So what were the problems?
To understand it must be clear the exercise schema the teacher was adopting, and that was pretty much always the same: a main script (let's call "coordinator" which aim is to parse the argument list and invoke, using recursion, a "worker" script.
Something that can be shown as the following code:

#!/bin/sh
# coordinator

# argument validation...

# export the current directory
# in the path to invoke the worker script
PATH=$PATH:`pwd`
export PATH

# first call of the worker
worker



#!/bin/sh
# worker

# recursion on myself
for f in *
do
if [ -d $f ]
then
worker f
fi
done

# do other work...



The first problem, in my opinion, is the usage of relative paths to invoke the worker script, and therefore the need for exporting the PATH variable. First of all, launching a script with a relative path makes it a little slower to launch, since the shell itself has to search for the script against each PATH entry. Second, and much more important, it is the key to exploitation: not having the control over the full path of the script it is possible to inject a malicious script somewhere in the PATH and use it as a worker.
When I objected the above to the teacher, the answer was to simply invert the PATH manipulation order:

PATH=`pwd`:$PATH
export PATH

But again, this is a kick in the ass of security: what if I name my script as another system wide command? I can alter the behaviour of this and other programs...
So what is the solution? Of course invoke the worker script with an absolute path and to not manipulate the PATH variable. After all, what is the point in showing (to the teacher) you can export a variable?

Another problem is the recursion on the worker script: usually such script was scanning a directory content, invoking itself each time a subdirectory was found. Now, while this can work in theory, you can easily imagine the worker script becoming a fork-bomb. It is quite easy to see how find(1), xargs(1) and friends can help in this situation.

Another oddity that comes into my mind is the way students were forced to test if an argument was an absolute path or a relative one:

case $1 in
/*) # absolute
;;
*) # relative
;;
esac


Do you believe the above is easy to read? Is it efficient and does it scale well? Why not using Unix tools and pipes, regular expressions and awk? even better, getopt anyone?

So, dear ex-teacher, what is the whole point in teaching such shit?

sabato 17 dicembre 2016

Cat avatar generator

Un'idea carina ed interessante per la generazione di avatar a forma di gatto: <http://peppercarrot.com/extras/html/2016_cat-generator/>.
Un po' ripetititivi, ma sicuramente di effetto.

venerdì 28 ottobre 2016

From File::Find to File::Find::Rule

I tend to use File::Find the most in order to get some file searching and mangling. Usually my scripts have the same simple structure as follows:

$| = 1; # autoflush
find( \&directory_scanner, ( $starting_directory ) );
$| = 0; # non autoflush

# and the scanner is something like
sub directory_scanner{
    chomp;

    return if ( $_ eq $starting_directory || ! -f $_ );
    return if ( $File::Find::dir !~ /$re_dir(\d{4}-\d{6})$/ );
    ...
}
 
As you can see the event handler invoked by File::Find is used to both print some report (the $counter) in order to tell me the script is still alive (I do pass 200+k files at once) but, most notably, applies a regexp to the directory I'm in in order to avoid some staging/backup/etc. directory that could be likely the one I'm interested into but I don't want the script to pass.
For a few times I've tried to convert my Find::File based scripts to File::Find::Rule, just to get more used with such interface, but I didn't know how to fix the application of regular expression to the traversing path. Reading a little more deeply the documentation I found the exec subroutine that allows me to specify an handler (i.e., a subroutine) that can return true or false depending on what I want to do on the file I'm visiting. Therefore, converting my scripts becomes as easy as follows:
 
$| = 1; 
my $engine = File::Find::Rule->new();
my @files  = $engine->file()
    ->exec( sub {
        my ( $shortname, $path, $fullname ) = @_;
        return $path !~ /$re_dir(\d{4}-\d{6})$/;
            } )
    ->exec( sub{
        my ( $shortname, $path, $fullname ) = @_;
        $counter++;
        return $shortname =~ /KCL/;
            } )
    ->exec( sub{
        my ( $shortname, $path, $fullname ) = @_;
        print "." if ( $counter % 100 == 0 );
        print "$counter\n" if ( $counter % 1000 == 0 );
        return 1; # do not forget !
            } )
    ->in( $starting_directory );
$| = 0; 
 
I've kept three different handlers for readibility sake, but as you can image, it is possible to shrink them down into a single one. The funny part here is that I can check the path against a regexp again. The drawback is that an handler used for output reporting only must return always a true value.
In the case you are wondering, the autoflush is used simply to display the dots while the program is running.

not $foo++

Sono inceppato nell'impementazione di List::MoreUtils::PP::uniq e ho notato una cosa che mi ha fatto sorgere un grosso punto di domanda: un uso quasi malsano dell'operatore not. Ma in fondo è questo il bello di Perl: riuscire ad esprimere concetti molto difficili con pochissimo codice. Ecco quindi il codice in esame:


sub uniq (@)
{
    my %seen = ();
    my $k;
    my $seen_undef;
    grep { defined $_ ? not $seen{ $k = $_ }++ : not $seen_undef++ } @_;
}
 
Il trucco che fa funzionare il tutto è in quel pezzo not $seen{ $k = $_ }++, e in particolare nella negazione. Provo a smontare il codice far comprendere come io sono giunto alla soluzione. Anzitutto la funzione grep processa la lista di paramentri e si aspetta un risultato true o false a seconda del fatto che lo si voglia restituire in uscita dalla funzione (essendo grep l'ultima istruzione effettuata si ha un return implicito). Se il parametro corrente $_ è valido (defined) lo si usa come chiave di un hash %seen e si incrementa (con autovivification) il valore della cella relativa. Fin qui tutto facile, ma occorre ricordarsi che l'operatore postfisso ++ restituisce il valore prima del suo incremento. In sostanza, la prima volta che si trova un elemento nella lista la relativa cella di %seen viene incrementata ma il valore restituito dall'operazione di ++ è zero (ossia falso). Questo valore viene passato all'operatore not che a sua volta lo riconverte in contesto boolean a true e l'elemento viene "greppato" nella lista di ritorno. Alla successiva occorrenza dello stesso elemento la sua cella in %seen viene incrementata, l'operatore ++ restituisce il vecchio valore (1) e questo viene forzato in contesto boolean (quindi true) e negato portando quindi il valore a false, scartando quindi l'elemento. Cosa analoga avviene per i valori undefined, che però in questo caso vengono considerati tutti uguali e quindi non si ha bisogno di tenerli come chiave di un hash.
Direi un bell'esempio di come Perl 5 possa portare a codice molto compatto che però richiede un po' di potenza mentale per essere interpretato!

giovedì 13 ottobre 2016

Hacktoberfest

Si, sto cercando di partecipare all'Hacktoberfest 2016.
E qui si trova il mio stato ad oggi, anche se non sto partecipando solo a patch #Hacktoberfest!

venerdì 5 agosto 2016

Coerenza dei nomi: un esempio (cattivo) basato su PHP

La coerenza nel nome delle variabili e delle funzioni è molto importante, me ne rendo conto ogni giorno di piu' e soprattutto sono capace ogni giorno di notare errori anche grossolani nelle implementazioni dei vari linguaggi di programmazione.
Uno degli errori, amio avviso, clamorosi nella implementazione di PHP è l'incoerenza dei nomi delle funzioni empty() e is_null(). Mentre l'ultima finisce ha un mnemonico che si legge intuitivamente in un test booleano, la prima no. Mi capita quindi abbastanza spesso di scrivere del codice come:

if ( is_empty( $foo ) ){ ... }

che ovviamente è sbagliato poichè la funzione è empty(). Da cosa nasce questa incoerenza? E' abbastanza semplice da capire: "null" in PHP è parola chiave e quindi la funzione relativa non poteva chiamarsi semplicemente null(), da qui l'esigenza di aggiungere il prefisso "is_". Ma questo doveva portare a trattare la duale funzione empty() con la stessa regola del nome, anche se "empty" non è (al momento) parola chiave. Dopotutto, come Perl Best Practice insegna, è bene che le condizioni booleane abbiano un nome che inizia con "is" o "has", e sicuramente è piu' facile da leggere "if is empty" che non "if empty".

martedì 27 ottobre 2015

Il modo corretto di uscire da Vi(m)

C'è una maglietta promozionale per Vim che riporta un comando errato, che trovo molto spesso anche nei manuali all'editor dedicati.


Spesso si trova, come metodo di uscita "sicura" dall'editor di fornire il comando ':wq!', che significa 'scrittura' (w) 'forzata' (!) ed 'uscita' (q); in altre parole si salva il file corrente e si esce.
Qual'è il problema? Che la scrittura forzata aggiorna i metadati del file anche quando questo non è stato modificato, mandando quindi in confusione programmi che digeriscono questi metadati (es. make). Il metodo corretto per uscire dall'editor è usare 'ZZ', che si preoccupa di salvare i file non salvati che sono stati modificati, lasciando inalterati gli altri.

venerdì 29 maggio 2015

Me, myself and Dataflex

I hate Dataflex http://en.wikipedia.org/wiki/DataFlex with a passion!
Well, when I say that I hate Dataflex I should say I hate the so called "console" mode Dataflex, the only one I've ever worked on. And I have to also say that part of my hate is due to a wrong training, or better, no training at all.

How did I ever meet Dataflex (df31d)? Well, you know, the rent is a good motivation to work with tools you don't like very much.
Since the beginning the language itself appeared awkward to me. Coming from some real languages (C, Perl, Java), I felt not at home with a language case-insensitive.
The total lack of braces and the return to the BEGIN-END syntax was quite a shock.
No multi-line comments.
A compiler that crashed each time you had a line longer than 256 characters...and no, I'm not joking! I don't remember how many hours I spent trying to understand why a program was not compiling at all, without any error message, to just discover that somewhere I had a quite complex IF clause (indented) that has exceed the right margin. And I have to say that I often laugth thinking at this stupid bug, probably implemented in a way I only have seen in didactic examples of C such as:

#define MAX_LINE_SIZE 256
...
char current_line[ MAX_LINE_SIZE ];

However, I started writing my programs, and as usual with a new language, my first developments were baby steps in the Dataflex world. My programs were simple, written in a simple and well documented way, so that they looked even more stupid to me.

There was not an IDE to develop in Dataflex, so I fired up my Emacs to the aim. But it was not a simple task, since Dataflex was displaying masks on the screen using the DOS character set, that at that time was not shipped with Emacs. I had therefore to compile the appropriate encoding, add to Emacs, and configure the editor to load such encoding for every Dataflex file (.frm). At that time I was at the very basic of Emacs, and so it was quite an hard job to me.

As I said, I was coming from some experience with real languages, and if you can pass over the syntax and the buggy compiler, you cannot live without methods. Well, my Dataflex was without methods. I had two choice: define a "routine", invoked via a far jump (GOSUB) or use labels to far jump to other pieces of the programs (GOTO).


The operator set was...tiny. Moreover, many operators were verbatim, so that comparisons use GT,GE,LE,LT and so on.
Assignment was performed via a MOVE...TO command. If my memory serves me well, the only "smart" arithmetic operators were INCREMENT and DECREMENT.

To complete the nightmare, I did not have any kind of good documentation (and I was not able to find out any on the Web).

But you have the opportunity to define "macros", in the C language sense. Ok, this sounds good, until you clash some variable or loop name.

Last but not least, the compiler was reporting errors at lines with macro expanded. In other words, while the compiler was reporting an error on line X, your error could be a lot before due to some macro expansion.

After a while I was working with all this mess, I found the special DEBUG command. The purpose was to print out to the screen at which line the program was executing. But it was not very helpful, since it was just printing out a number (like 123) on the screen exactly where the cursor was, so filling your application of digits making me feeling I was looking at the Matrix screen.

Next I discovered the -v switch on the compiler, and I found it could be increased at least to -vvv to get more verbose messages. Or better, the messages were obscure as usual, but the processed file (with macro expanded) was printed on the screen, so that you can find out the line number with more accuracy.

Then came methods. Yeah! A great day, one that made me feel a bit more at home.
Well, methods in Dataflex are not what you would expect from other languages. The prototype is extremely verbose, the invocation reminds me to lisp since you have to put the method name in parentheses (as well as arguments):

(foo(1, 2, 3))

But hey, at least you have some real reusable code without the name clashing of a macro and with a return value!

The special key handling was a pure mess! Dataflex used subroutines to handle events generated by special keys, with some confusion on what, when, and how to resume the control flow.

The database structure was...not a database structure, at least in my opinion.
If my memory serves me well, you had to define a new archive (kind of ISAM) using a specific program, that ensured that the data file and the indexes (also separated files) where in place. The fresh archive was then added to the so called filelist, that was in charge of listing all available databases (it was a kind of schema to a RDBMS). Modifying an archive (e.g., adding a field) was of course a locking operation, so you had to schedule for maintanance. And being the filelist limited in size, you had a limited number of tables/archives in your deployment.

One way to overtake the number of archive limitations was to play with user paths: as happens with the concept of search schema in RDBMS, an user can have several copy of the same archive, with the same binary structure and different content, in different disk positions. Pointing to one or another would do the trick.
We used this in particular to scatter a few utility archives among users, so that every one could have its own copy.

Relying on the file system, data corruption was dumpered by the operating system and its own file system. Using Linux, luckily, there were no many corruptions, but ship happens, and so you had to run a specific tool to reindex the whole archive. Of course, this was another full-locking operation.

In general, the speed of data retrieval was good, but the approach was that of single record (opposed to the one of whole set), and therefore all programs contained long and nested loops to extract the information you need. The relational part of the query (e.g., join) was all in charge of the developer, and therefore missing a single attribute could destroy all your retrieve logic in a subtle way.

Ok, so there were BEGIN-END, loops, GOTO/GOSUB and locking operations...but the system workded. And it worked up to a few gigabytes of data, therefore I have to say I was quite impressed about.

Of course, you did not have the flexibility of SQL, and you did not have even a way to specify a query that was not "pre-built". Allow me to elaborate a bit more: as I said, you had to define indexes for every archive. An index defines how the archive can be read, that is in which order you can loop thru the records. What if you want to retrieve the record in another order? You have to either define a new index (but you are limited by number of indexes and locking operations) or to use existing ones in an esotheric way, making your loops even more complex and your program a lot less readable.

Last but no least: an index was not only an access method, but a way to define an unique constraint. Therefore, you were locked to only a few indexes for every archive, and the rest was a huge REPL.
There was also the catch-all looping mechanism: the sequential scan of the whole archive (also known as BY RECNUM).

Adding records was quite simple, a special instruction SAVERECORD was there for the aim.
Modifying a record was a little more complex, since you had to lock via the REREAD command, modify fields and then issue a SAVERECORD followed by an UNLOCK.

Perl came to a rescue.
At that time I was mainly using shell scripting, but here I needed something a little more complex to handle all the mess left around by Dataflex. For instance, I used a Perl script to convert and mangle output text before sending it to the printer. Dataflex was absolutely not good at handling text!
I also used Perl to control how the users jumped into the Dataflex runtime, and this allowed us to ease the management of the sessions when locking operations were absolutely necessary.
Finally, I used Perl to mangle some Dataflex source code in order to avoid some boring stuff.

I have to say that, in order to automate some looping, Dataflex provided WINDOWINDEX and FIELDINDEX, two special (global) varaibles to iterate over UI and database fields. Please note that the above variables were global, so a wrong initialization could make you fly to the wrong record or field!

Now, after all this mess, I have to say that I'm aware of a lot of good uses of Dataflex, that has also a kind of OOP interface. As I told, I had to work on a lot of legacy code, and without documentation and appropriate training, it was quite impossible for me to use "advanced" features.
As final word, please note that Dataflex was a quite old language, therefore it is obvious that when compared to modern languages it looks scaring and awkward.

But sometimes I still have nightmares about Dataflex!

martedì 17 marzo 2015

Respect the developers time and wills!

I believe there is a strong understimation of Open Source work today: people complains more and more everyday without taking into account that who does Open Source is donating her time and efforts to the whole community.
Having stated that, it is really simple, in my very own opinion, that developers have the rights to work on what make them happy, not on what will make the users' happy.
Most of the time, the two side of happiness coincide.
In this scenario, before expressing rage against a group of developers without providing help, it is better to remember that you, the final user, did not hire the developers themselves, and therefore they are free to ignore you.
For a pratical example, see here  and if you have a little respect for yourself, never reply like this!

lunedì 16 marzo 2015

About programming competitions and selections

I found this very interesting post l about online competitions for computer programmers.
Taking part to the 2015 CPAN Pull Request Challenge, and agreeding totally to the post author about how useless these competitions can be, I want to enforce the concept.
Nowdays softwares are very complex beasts, and it is much more important to have a look at how to solve a problem in a way that can be maintanable, well documented, self explainatory, and so on, rather than having a bunch of code that performs the right computation with a strange and not well known alghoritm. Moreover, with the ubiquity of alghoritm libraries, I don't see the whole point in proving your knowledge of alghoritms anymore.

I remember at least three job interviews I made a few years ago when I was asked to solve a problem implementing a mathematical alghoritm. And I failed.
But I also remember at least two of the interviewers telling me they don't know git and FreeBSD, or don't knowing the difference between a log-shipping replication and a streaming one.
I tend to prefer to know a little about a lot of things, so to be able to choose the right tool at the right moment (and improve my skills on demand), rather than knowing a single tool/paradigm very well.

So I don't see the aim of having an online competition on alghoritms, or even asking anymore alghoritms (except if your business is based on those). Rather, I strongly believe that being able to prove you collaborated in FLOSS projects, have pushed changes and commits to real code makes you resume stronger.