Traduzione in italiano di Domenico Pastore dall’essay originale di Paul Graham "Succinctness is Power" [Maggio 2002].
"La quantità di significato compressa in un piccolo spazio dai segni algebrici è un'altra circostanza che facilita i ragionamenti che siamo abituati a portare avanti con il loro aiuto".
- Charles Babbage, citato nella conferenza del Premio Turing di Iverson.
Nella discussione sui problemi sollevati da Revenge of the Nerds sulla mailing list LL1, Paul Prescod ha scritto qualcosa che mi è rimasto in mente.
L'obiettivo di Python è la regolarità e la leggibilità, non la sinteticità.
A prima vista, questa sembra un'affermazione piuttosto negativa per un linguaggio di programmazione. Per quanto ne so, sinteticità = potenza. Se così fosse, con le opportune sostituzioni, otterremmo
L'obiettivo di Python è la regolarità e la leggibilità, non la potenza.
E questo non sembra un compromesso (se di compromesso si tratta) che si voglia fare. Non è lontano dal dire che l'obiettivo di Python non è quello di essere efficace come linguaggio di programmazione.
Sinteticità = potenza? Questa mi sembra una domanda importante, forse la più importante per chiunque sia interessato alla progettazione di un linguaggio, e una domanda che sarebbe utile affrontare direttamente. Non sono ancora sicuro che la risposta sia un semplice sì, ma mi sembra una buona ipotesi da cui partire.
Ipotesi
La mia ipotesi è che la sinteticità sia la potenza, o che sia abbastanza vicina da poter essere considerata identica, tranne che in esempi patologici.
Mi sembra che la sinteticità sia lo scopo dei linguaggi di programmazione. I computer sarebbero altrettanto felici di sentirsi dire cosa fare direttamente in linguaggio macchina. Credo che il motivo principale per cui ci prendiamo la briga di sviluppare linguaggi di alto livello sia quello di ottenere un effetto leva, in modo da poter dire (e soprattutto pensare) in 10 righe di un linguaggio di alto livello ciò che richiederebbe 1000 righe di linguaggio macchina. In altre parole, lo scopo principale dei linguaggi di alto livello è quello di ridurre il codice sorgente.
Se la riduzione del codice sorgente è lo scopo dei linguaggi di alto livello, e la potenza è quanto bene raggiunge il suo scopo, allora la misura della potenza di un linguaggio di programmazione è quanto rende piccoli i vostri programmi.
Al contrario, un linguaggio che non rende piccoli i programmi fa un pessimo lavoro rispetto a ciò che i linguaggi di programmazione dovrebbero fare, come un coltello che non taglia bene o una stampa illeggibile.
Metriche
Piccolo in che senso? La misura più comune della dimensione del codice è rappresentata dalle linee di codice. Ma credo che questa metrica sia la più comune perché è la più facile da misurare. Non credo che nessuno creda davvero che sia il vero test della lunghezza di un programma. Linguaggi diversi hanno convenzioni diverse per quanto riguarda la quantità di codice da inserire in una riga; in C molte righe non contengono altro che uno o due delimitatori.
Un altro test facile è il numero di caratteri in un programma, ma anche questo non è molto buono; alcuni linguaggi (Perl, per esempio) usano identificatori più corti di altri.
Credo che una misura migliore della dimensione di un programma sia il numero di elementi, dove un elemento è qualsiasi cosa che costituirebbe un nodo distinto se si disegnasse un albero che rappresenta il codice sorgente. Il nome di una variabile o di una funzione è un elemento; un numero intero o in virgola mobile è un elemento; un segmento di testo letterale è un elemento; un elemento di uno schema o di una direttiva di formato è un elemento; un nuovo blocco è un elemento. Ci sono casi limite (-5 è un elemento o due?), ma credo che la maggior parte di essi sia uguale per tutte le lingue, quindi non influenzano molto i confronti.
Questa metrica deve essere perfezionata e potrebbe richiedere un'interpretazione nel caso di linguaggi specifici, ma credo che cerchi di misurare la cosa giusta, cioè il numero di parti di un programma. Penso che l'albero che si disegna in questo esercizio sia quello che si deve creare nella propria testa per concepire il programma, e quindi la sua dimensione è proporzionale alla quantità di lavoro che si deve fare per scriverlo o leggerlo.
Design
Questo tipo di metrica ci permetterebbe di confrontare linguaggi diversi, ma non è questo, almeno per me, il suo valore principale. Il valore principale del test di sinteticità è come guida nella progettazione delle lingue. Il confronto più utile tra le lingue è quello tra due potenziali varianti della stessa lingua. Cosa posso fare nel linguaggio per rendere i programmi più brevi?
Se il carico concettuale di un programma è proporzionale alla sua complessità, e un dato programmatore può tollerare un carico concettuale fisso, allora questo equivale a chiedersi: cosa posso fare per consentire ai programmatori di ottenere il massimo? E questo mi sembra identico alla domanda: come posso progettare un buon linguaggio?
(Per inciso, niente rende più evidente che la vecchia credenza "tutti i linguaggi sono equivalenti" è falsa quanto la progettazione di linguaggi. Quando si progetta un nuovo linguaggio, si confrontano costantemente due linguaggi - il linguaggio se facessi x e quello se non lo facessi - per decidere quale sia migliore. Se questa fosse davvero una domanda senza senso, tanto varrebbe lanciare una moneta).
Puntare alla sinteticità sembra un buon modo per trovare nuove idee. Se si riesce a fare qualcosa che accorcia molti programmi diversi, probabilmente non è una coincidenza: probabilmente si è scoperta una nuova astrazione utile. Potreste anche essere in grado di scrivere un programma che vi aiuti nella ricerca di schemi ripetuti nel codice sorgente. Tra gli altri linguaggi, quelli che hanno una reputazione di sinteticità sono quelli a cui guardare per trovare nuove idee: Forth, Joy, Icon.
Confronto
Il primo a scrivere su questi temi, per quanto ne so, è stato Fred Brooks nel Mese dell'Uomo Mitico. Egli scrisse che i programmatori sembravano generare più o meno la stessa quantità di codice al giorno, indipendentemente dal linguaggio. Quando lo lessi per la prima volta, all'inizio dei miei vent'anni, fu una grande sorpresa per me e mi sembrò che avesse enormi implicazioni. Significava (a) che l'unico modo per scrivere software più velocemente era usare un linguaggio più sintetico e (b) chi si prendeva la briga di farlo poteva lasciare nella polvere i concorrenti che non lo facevano.
L'ipotesi di Brooks, se fosse vera, sembra essere al centro dell'hacking. Negli anni successivi, ho prestato molta attenzione a tutte le prove che ho potuto ottenere sulla questione, da studi formali ad aneddoti su singoli progetti. Non ho visto nulla che lo contraddica.
Non ho ancora visto prove che mi sembrassero conclusive, e non mi aspetto di vederle. Studi come il confronto tra linguaggi di programmazione di Lutz Prechelt, pur generando il tipo di risultati che mi aspettavo, tendono a utilizzare problemi troppo brevi per essere test significativi. Un test migliore di un linguaggio è quello che accade nei programmi che richiedono un mese di tempo per essere scritti. E l'unico vero test, se si crede come me che lo scopo principale di un linguaggio sia quello di essere buono per pensare (piuttosto che per dire a un computer cosa fare una volta che lo si è pensato) è quali nuove cose si possono scrivere in esso. Quindi, qualsiasi confronto tra linguaggi in cui si debba soddisfare una specifica predefinita è un test leggermente sbagliato.
Il vero test di un linguaggio è la capacità di scoprire e risolvere nuovi problemi, non la capacità di usarlo per risolvere un problema già formulato da qualcun altro. Si tratta di due criteri molto diversi. Nell'arte, mezzi come il ricamo e il mosaico funzionano bene se si sa in anticipo cosa si vuole realizzare, ma sono assolutamente pessimi se non lo si sa. Quando si vuole scoprire l'immagine man mano che la si realizza - come si deve fare con qualsiasi cosa complessa come l'immagine di una persona, per esempio - è necessario usare un mezzo più fluido come la matita o la china o la pittura a olio. In effetti, il modo in cui si realizzano arazzi e mosaici è quello di creare prima un dipinto e poi di copiarlo. (La parola "cartone animato" era originariamente usata per descrivere un dipinto destinato a questo scopo).
Ciò significa che probabilmente non avremo mai confronti accurati sulla potenza relativa dei linguaggi di programmazione. Avremo confronti precisi, ma non accurati. In particolare, gli studi espliciti con lo scopo di confrontare i linguaggi, poiché probabilmente utilizzeranno problemi di piccole dimensioni e necessariamente predefiniti, tenderanno a sottostimare la potenza dei linguaggi più potenti.
I risultati sul campo, anche se necessariamente meno precisi degli studi "scientifici", sono probabilmente più significativi. Ad esempio, Ulf Wiger di Ericsson ha condotto uno studio che ha concluso che Erlang è da 4 a 10 volte più sintetico di C++ e proporzionalmente più veloce nello sviluppo di software:
I confronti tra i progetti di sviluppo interni a Ericsson indicano una produttività simile in termini di linee/ore, comprese tutte le fasi di sviluppo del software, indipendentemente dal linguaggio utilizzato (Erlang, PLEX, C, C++ o Java). Ciò che differenzia i diversi linguaggi diventa quindi il volume del codice sorgente.
Lo studio affronta anche esplicitamente un punto che nel libro di Brooks era solo implicito (dato che misurava le linee di codice debuggato): i programmi scritti in linguaggi più potenti tendono ad avere meno bug. Questo diventa un fine in sé, forse più importante della produttività dei programmatori, in applicazioni come gli switch di rete.
Il test del gusto
In definitiva, credo che si debba seguire il proprio istinto. Cosa si prova a programmare in quel linguaggio? Credo che il modo per trovare (o progettare) il miglior linguaggio sia quello di diventare ipersensibili al modo in cui un linguaggio vi permette di pensare, quindi scegliere/progettare il linguaggio che vi fa sentire meglio. Se qualche caratteristica del linguaggio è scomoda o limitante, non preoccupatevi, lo saprete.
Questa ipersensibilità avrà un costo. Scoprirete di non sopportare la programmazione in linguaggi goffi. Trovo estremamente restrittivo programmare in linguaggi senza macro, così come chi è abituato alla tipizzazione dinamica trova estremamente restrittivo dover tornare a programmare in un linguaggio in cui si deve dichiarare il tipo di ogni variabile e non si può fare un elenco di oggetti di tipo diverso.
Non sono l'unico. Conosco molti hacker Lisp a cui è successo. In effetti, la misura più accurata del potere relativo dei linguaggi di programmazione potrebbe essere la percentuale di persone che conoscono il linguaggio e che accetterebbero qualsiasi lavoro in cui si possa usare quel linguaggio, indipendentemente dal dominio applicativo.
Restrittività
Credo che la maggior parte degli hacker sappia cosa significhi che un linguaggio è restrittivo. Cosa succede quando lo sentite? Penso che sia la stessa sensazione che si prova quando la strada che si vuole percorrere è bloccata e si deve fare una lunga deviazione per arrivare dove si vuole andare. C'è qualcosa che vuoi dire, ma la lingua non te lo permette.
Credo che il vero problema sia che un linguaggio restrittivo non è abbastanza sintetico. Il problema non è semplicemente che non si può dire quello che si voleva dire. È che la deviazione che il linguaggio vi fa fare è più lunga. Provate a fare questo esperimento di pensiero. Supponiamo che si voglia scrivere un programma e che il linguaggio non permetta di esprimerlo nel modo previsto, ma costringa a scriverlo in un altro modo che sia più breve. Almeno per me, questo non sarebbe molto restrittivo. Sarebbe come se la strada che volevate percorrere fosse bloccata e il poliziotto all'incrocio vi indicasse una scorciatoia invece di una deviazione. Ottimo!
Credo che la maggior parte (il novanta per cento forse?) della sensazione di restrittività derivi dall'essere costretti a rendere il programma che si scrive nel linguaggio più lungo di quello che si ha in testa. La restrittività è soprattutto mancanza di sinteticità. Quindi, quando un linguaggio sembra restrittivo, ciò che (per lo più) significa è che non è abbastanza sintetico, e quando un linguaggio non è sintetico, si sentirà restrittivo.
Leggibilità
La citazione con cui ho iniziato cita altre due qualità, la regolarità e la leggibilità. Non sono sicuro di cosa sia la regolarità, né di quale vantaggio abbia, se ce n'è uno, un codice regolare e leggibile rispetto a un codice semplicemente leggibile. Ma credo di sapere cosa si intenda per leggibilità, e penso che sia anche legata alla sinteticità.
Dobbiamo fare attenzione a distinguere tra la leggibilità di una singola riga di codice e la leggibilità dell'intero programma. È la seconda che conta. Sono d'accordo che una riga di Basic è probabilmente più leggibile di una riga di Lisp. Ma un programma scritto in Basic avrà un numero maggiore di righe rispetto allo stesso programma scritto in Lisp (soprattutto quando si passa a Greenspunland). Lo sforzo totale di lettura del programma Basic sarà sicuramente maggiore.
sforzo totale = sforzo per riga x numero di righe
Non sono così sicuro che la leggibilità sia direttamente proporzionale alla sinteticità come lo sono della potenza, ma certamente la sinteticità è un fattore (in senso matematico; si veda l'equazione precedente) della leggibilità. Quindi potrebbe anche non avere senso dire che l'obiettivo di un linguaggio è la leggibilità, non la sinteticità; potrebbe essere come dire che l'obiettivo è la leggibilità, non la leggibilità.
Ciò che la leggibilità per riga significa, per l'utente che incontra il linguaggio per la prima volta, è che il codice sorgente avrà un aspetto non ostile. Quindi la leggibilità per riga potrebbe essere una buona decisione di marketing, anche se è una cattiva decisione di progettazione. È isomorfa alla tecnica di grande successo di far pagare a rate: invece di spaventare i clienti con un prezzo anticipato elevato, si comunica il basso pagamento mensile. I piani rateali, però, sono una perdita netta per l'acquirente, come probabilmente lo è la semplice leggibilità per riga per il programmatore. L'acquirente effettuerà molti pagamenti bassi, e il programmatore dovrà leggere molte di quelle righe leggibili singolarmente.
Questo compromesso è precedente ai linguaggi di programmazione. Se siete abituati a leggere romanzi e articoli di giornale, la prima esperienza di lettura di un documento di matematica può essere sconcertante. Potrebbe volerci mezz'ora per leggere una sola pagina. Eppure, sono abbastanza sicuro che il problema non sia la notazione, anche se può sembrare cosi'. Se si esprimessero le stesse idee in prosa (come dovevano fare i matematici prima di evolvere le notazioni sintetiche), non sarebbero più facili da leggere, perché il documento diventerebbe grande come un libro.
Fino a che punto?
Alcune persone hanno respinto l'idea che sinteticità = potenza. Credo che sarebbe più utile, invece di discutere semplicemente che sono o non sono la stessa cosa, chiedersi: fino a che punto la sinteticità = potenza? Perché è chiaro che la sinteticità è una parte importante dello scopo dei linguaggi di livello superiore. Se non è l'unica cosa a cui servono, allora a cos'altro servono e quanto sono importanti, relativamente, queste altre funzioni?
Non sto proponendo questo solo per rendere il dibattito più civile. Voglio davvero conoscere la risposta. Quando, se mai, un linguaggio è troppo sintetico per il suo fine?
L'ipotesi da cui sono partito è che, salvo esempi patologici, ritengo che la sinteticità possa essere considerata identica alla potenza. Intendevo dire che in qualsiasi linguaggio che si possa progettare, le due cose sarebbero identiche, ma che se qualcuno volesse progettare un linguaggio esplicitamente per smentire questa ipotesi, probabilmente potrebbe farlo. In realtà non ne sono nemmeno sicuro.
Linguaggi, non programmi
È bene chiarire che stiamo parlando della sinteticità dei linguaggi, non dei singoli programmi. È certamente possibile che i singoli programmi siano scritti in modo troppo denso.
Ne ho scritto in On Lisp. Una macro complessa potrebbe dover risparmiare molte volte la sua stessa lunghezza per essere giustificata. Se scrivere una macro complessa può far risparmiare dieci righe di codice ogni volta che la si usa, e la macro stessa è composta da dieci righe di codice, allora si ottiene un risparmio netto di righe se la si usa più di una volta. Ma potrebbe comunque essere una mossa sbagliata, perché le definizioni delle macro sono più difficili da leggere rispetto al codice ordinario. Si potrebbe dover usare la macro dieci o venti volte prima di ottenere un miglioramento netto della leggibilità.
Sono sicuro che ogni linguaggio ha dei compromessi di questo tipo (anche se sospetto che la posta in gioco diventi più alta man mano che il linguaggio diventa più potente). Ogni programmatore deve aver visto del codice che qualche persona intelligente ha reso marginalmente più breve utilizzando trucchi di programmazione discutibili.
Quindi non c'è alcuna discussione in merito, almeno non da parte mia. I singoli programmi possono certamente essere troppo sintetici per il loro bene. La domanda è: può un linguaggio esserlo? Può un linguaggio costringere i programmatori a scrivere codice breve (in elementi) a spese della leggibilità complessiva?
Uno dei motivi per cui è difficile immaginare un linguaggio troppo sintetico è che se ci fosse un modo eccessivamente compatto per formulare qualcosa, probabilmente ci sarebbe anche un modo più lungo. Per esempio, se ritenete che i programmi Lisp che utilizzano molte macro o funzioni di ordine superiore siano troppo densi, potreste, se preferite, scrivere codice isomorfo al Pascal. Se non si vuole esprimere il fattoriale in Arc come una chiamata a una funzione di ordine superiore
(rec zero 1 * 1-)
è anche possibile scrivere una definizione ricorsiva:
(rfn fact (x) (if (zero x) 1 (* x (fact (1- x)))))
Anche se non mi viene in mente nessun esempio, sono interessato alla questione se un linguaggio possa essere troppo sintetico. Ci sono linguaggi che costringono a scrivere codice in un modo che risulta incomprensibile? Se qualcuno ha degli esempi, sarei molto interessato a vederli.
(Ricordare: Quello che sto cercando sono programmi molto densi secondo la metrica degli "elementi" abbozzata sopra, non semplicemente programmi che sono brevi perché i delimitatori possono essere omessi e tutto ha un nome di un solo carattere).
This is a public episode. If you would like to discuss this with other subscribers or get access to bonus episodes, visit paulgrahamita.substack.com