Zėŋōfōbìå

29 gennaio 2009

Contratti e futuro

Filed under: informatica — Zeno @ 10:27

Scrivere programmi è relativamente facile, fare in modo che funzionino sempre è invece praticamente impossibile. Bisogna convivere con gli errori e cercare di limitarne i casi d’impatto sull’utente.

Un approccio possibile è quello di costruire un percorso stabile di funzionalità, definire una procedura d’utilizzo del programma che permetta di non uscire da quella strada, educare l’utente ad attenersi alle procedure e infine, in caso di problemi, esordire dicendo: “sicuramente l’utente non ha seguito alla lettera le procedure”.

Ovviamente sarebbe auspicabile dare all’utente il diritto di sbagliare, perché tanto, prima o poi sbaglierà.

Prevedere tutti i casi possibili è impossibile, un buon programma riconosce i percorsi validi e segnala con un errore i percorsi imprevisti. Devi fare la somma di due numeri e, per qualche ragione scritta nei requisiti non ti permetti di definire la somma per due stringhe? Occorre essere certi che gli argomenti della funzione siano effettivamente numeri e che anche la loro somma lo sia e che il risultato sia corretto. In altre parole bisogna verificare che gli input della funzione siano corretti. (La CWE lo considera l’errore più grave e diffuso nella sua Top 25 Most Dangerous Programming Errors)

Meno banale di quanto appaia.

Credo che il linguaggio debba aiutare il programmatore a definire gli insiemi che usa nel modo più preciso possibile e per farlo usa i tipi. Un tipo è un insieme. “Nella definizione di tipo la parola insieme deve essere presente tra le prime 5 parole.”, insegna il maestro. Definire bene gli insiemi è un’operazione fondamentale, perché la validazione dell’input potrebbe limitarsi a verificare l’appartenenza all’insieme.

Un linguaggio fortemente tipato staticamente offre la verifica a tempo di compilazione che “certe” garanzie sui tipi siano valide. Tuttavia, in molti casi, i tipi di base non sono sufficienti e la tendenza nei linguaggi è stata quella di fornire strumenti atti alla definizione di tipi di dato astratto, ovvero tipi di dato definiti dal programmatore. La programmazione ad oggetti si è mossa nella stessa direzione, aggiungendo metodi per lavorare sui tipi in modo più dettagliato. Strumenti dei linguaggi che permettono di descrivere meglio gli insiemi dei dati da trattare.

Ma ancora manca qualcosa.

Torniamo al caso della somma di due interi, vediamo che i tipi di base possono essere insufficienti, sappiamo che la codifica degli interi ammette un massimo e che il suo superamento ha effetti algebricamente bizzarri: la somma di due interi può avere un risultato negativo. Overflow. Come possiamo evitare che accada? Runtime è facile, verifichiamo che il risultato sia coerente con le regole algebriche. Ci piazziamo un’assert all’uscita di funzione che sgancia l’allarme in caso di problemi.

Verifica runtime significa dover fare dei test runtime, che di solito occupano risorse di qualche tipo. Un tizio che faccia i test, che in ogni casi ha un costo, un programma che faccia i test, che comunque ha un prezzo.

La tendenza, nello stile di programmazione agile, che è la risposta ai tempi che corrono, è quello di muoversi verso l’approccio “test driven“, ovvero: ogni riga di codice deve avere un programma che ne verifica il funzionamento. Bene. Cioè, male. Voglio dire, che senso ha verificare che un intero sia minore del suo massimo? Che senso ha verificare che un booleano abbia valore vero oppure falso e “tertium non datur”? Ha senso solo nel momento in cui non si possa dare una garanzia “statica”, ovvero compile time, della consistenza degli insiemi. Se ci fosse la possibilità di fornire verifica formale della consistenza dei tipi, dell’appartenenza delle variabili ai tipi (variabili in senso lato…) , probabilmente si potrebbe risparmiare un sacco di risorse spese per farne i test.

Ada ha i range. Quando definisci un intero ne definisci anche il minimo e il massimo. Puoi scrivere cose del tipo:

A range is a signed integer value which ranges from a First to a last Last. It is defined as

 range First .. Last

Non è molto, ma consente di trovare un errore compile time qualora si cerchi di attribuire un valore ad una variabile che esca dal range specificato. Compile time.

Come mai questo approccio così semplice non sia stato definito nei linguaggi C, C++ e C# non lo capisco.

Qualcosa tuttavia deve essere fatto, e qualcosa si può fare.

Evolvere verso il Design by Contract, nello stile di Eiffel.

Quello che si ottiene è un’estensione del linguaggio, fornita dal compilatore, in grado di fornire, in modo coerente e facilmente documentabili, strumenti che permettono di aggiungere controlli statici e runtime sul linguaggio definiti dall’utente.
Io credo che la direzione sia questa, C# 4.0 includerà alcune funzionalità di questo tipo, C++0x prevede le asserzioni statiche, i concept e gli axiom, che in qualche modo vanno in questa direzione.

Stiamo a vedere.

Annunci

6 commenti »

  1. Premetto che concordo sul tuo discorso ma avrei 2 osservazioni…

    …sulle “tecniche”

    Il “maestro” che citi tempo fa (leggi: anni) mi ha regalato (GULP!) un bel libro sulle tecniche per evitare errori, per la precisione “Tecniche e trucchi per programmare senza errori (Maguire Steve, 1993)“.

    In questo libro, ora introvabile, questo guru mostra in modo molto scherzoso ma accurato come proteggersi dai problemi che citi e lui ricorreva alle semplici quanto bistrattate “assert”.

    …sulla “visione”

    Dopo aver letto il geniale “Perchè il software fa schifo…” (che poi ho recensito perchè merita tantissimo, IMHO) la mia visione del concetto di “errore” è profondamente mutata.

    Spesso non è il cliente che sbaglia o non conosce una nostra porocedura ma spesso è il nostro software così anti-intuitivo per un utente non-tecnico da metterlo in condizione di farsi e farci male. L’utente usa qualcosa e stop.

    Il discorso “come prevengo un errore” dovrebbe essere un discorso tipo “ultima chance”, quando tutto il resto fallisce.

    Non sono esattamente un fan della programmazione difensiva (lo ero ma nel tempo ho cambiato idea, complici molti libri validi). Tavolta noto che la programmazione difensiva “a priori” è usata solo come una scusa per proteggerci da un design penoso del codice che noi stessi scriviamo… ^^’

    Prima il design pulito, poi il resto. Se tentare le ottimizzazioni antitempo è male (Knuth docet), lo deve per forza essere anche l’aggiungere tonnellate di codice per proteggerci anzitempo da pericoli futuri…

    Ciau! :D

    Commento di jp — 29 gennaio 2009 @ 11:01

  2. Con le assert si fanno cose importanti. Ma ci vuole una buona infrastruttura.
    La questione della visione di cui parli la condivido, l’ho letta anch’io ma non ricordo dove. Le assert ai posti giusti aiutano il programmatore a capire esattamente cosa vuole, non è sempre così scontato. E sono anche sicuro che un buon design aiuta: invece di verificare che le classi siano consistenti bisognerebbe fare in modo che non possano non esserlo.

    Commento di Fabrizio — 29 gennaio 2009 @ 16:07

  3. Esattamente. Ad esempio Bernstein ha pensato e progettato bene i suoi programmi (Qmail, …) fin dall’inizio. Non li ha scritti e poi riempiti di tonnellate di controlli o fix…

    Ciau! ^^

    Commento di jp — 30 gennaio 2009 @ 12:03

  4. Bernstein è un genio. Io, purtroppo per me, non posso permettermelo.

    Commento di Fabrizio — 2 febbraio 2009 @ 17:07

  5. Nemmeno io posso anche solo osare ambire di arrivare al livello di Berstein…

    Ma mi piace osservare che lui ha dimostrato, in maniera evidente, che il design accurato di un qualcosa, supera enormemente la somma fra design “senza lode e senza infamia” e programmazione difensiva. :D

    Ciau! :D

    Commento di jp — 2 febbraio 2009 @ 21:00

  6. […] ho scritto in un commento al bel post di Zeno sul DbC: se tentare le ottimizzazioni antitempo è male (Knuth docet), lo deve per forza essere […]

    Pingback di Debug e principio di indeterminazione… « JP’s Web Place — 9 febbraio 2009 @ 8:21


RSS feed for comments on this post. TrackBack URI

Rispondi

Effettua il login con uno di questi metodi per inviare il tuo commento:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

Crea un sito o un blog gratuitamente presso WordPress.com.

%d blogger hanno fatto clic su Mi Piace per questo: