Le avventure di un Pythonista in Schemeland /1

Questo è il primo di una serie di articoli che raccontano le mie esperienze con il linguaggio Scheme, venendo da Python. Lo scopo di questa serie non è tanto quello di insegnare Scheme (anche se leggendo questi articoli qualche cosa si dovrebbe imparare) quanto quello di allargare gli orizzonti per chi non conosce altro che i linguaggi più gettonati.

Premessa: a chi si rivolge questa serie

Come si può capire facilmente dal titolo, in questa serie io adotterò l’ottica del Pythonista. Ciò nonostante, questi articoli dovrebbero essere perfettamente leggibili anche per programmatori Perl, Ruby, PHP, Tcl, eccetera. In generale tutti questi linguaggi (che chiamerò linguaggi dinamici mainstream) sono molto simili: interpretati, completamente dinamici, con un ottimo supporto per lo scripting.

Scheme è differente: pur essendo dinamico quanto i linguaggi appena citati e pur avendo in molte implementazioni un ottimo supporto per lo scripting, spesso e volentieri è anche compilato e può rivaleggiare con il C come velocità di esecuzione. Inoltre, Scheme è anche un linguaggio funzionale ed utilizza tutta una serie di idiomi caratteristici che non sono diffusi nei linguaggi mainstream. Infine, Scheme dispone di una macrologia molto avanzata e di vari costrutti (come per esempio le continuazioni) che non hanno equivalente in altri linguaggi. Questo rende lo studio di Scheme un’impresa non banale: è necessario essere molto motivati per riuscire a padroneggiare Scheme.

Se pensate che un linguaggio di programmazione sia semplicemente uno strumento per svolgere un determinato lavoro nel minor tempo possibile, e il vostro lavoro non è il ricercatore universitario, allora certamente non vi conviene imparare Scheme. D’altra parte se vi interessa capire quali sono le maniere possibili di svolgere lo stesso lavoro e valutare i pro e i contro dei diversi sistemi, allora probabilmente vi conviene imparare Scheme.

Sia chiaro che facendo questo distinzione io non voglio affatto implicare che il primo modo di pensare sia inferiore al secondo o viceversa: entrambi gli atteggiamenti sono ugualmente validi, a seconda delle circostanze. Considerate per esempio il caso di un bioinformatico impegnato a studiare una cura per una malattia genetica: certo è preferibile che si occupi di risolvere il suo problema piuttosto che si disperda a studiare tutti i possibili algoritmi di soluzione. D’altra parte, se si sta parlando un professore di Informatica non ci si aspetta certo che si fossilizzi su di un’unico linguaggio nè che conosca un unico approccio alla programmazione.

I Pythonisti, generalmente parlando, hanno una posizione intermedia: sono persone pragmatiche che vogliono portare a termine un lavoro concreto, ma che non si accontentano del primo strumento che trovano, altrimenti rimarrebbero per tutta la vita programmatori Visual Basic, Java o C++.

Questa serie di articoli si rivolge ai Pythonisti (e non) che si ritrovano nella descrizione che ho appena dato, e si propone di illustrare alcune caratteristiche del linguaggio di programmazione Scheme, con lo scopo di solleticare la curiosità e far pensare se si può imparare qualcosa da questo linguaggio che i più pensano destinato esclusivamente al mondo accademico.

Della lunga storia della programmazione: Fortran e Lisp

La storia della programmazione (lungi da me l’idea di scrivere una storia della programmazione in un paragrafo: quello che scriverò qui è una versione ultra semplificata e in parte inesatta della realtà, ma dovrebbe essere sufficiente per lo scopo di questa introduzione) comincia con due linguaggi da cui tutti gli altri sono discesi e che non potrebbero essere più diversi tra di loro in filosofia e campo di applicazione: il Fortran e il Lisp.

Entrambi i linguaggi escono dal mondo accademico, ma da due campi opposti: da una parte troviamo fisici e ingegneri interessati a calcoli numerici da effettuare nel modo più efficiente possibile per poter risolvere problemi concreti di fisica/ingegneria; dall’altra parte troviamo matematici interessati allo sviluppo di algoritmi per la soluzione di problemi astratti come il calcolo simbolico, la dimostrazione automatica di problemi, l’intelligenza artificiale e altre tematiche affini.

Entrambe le parti annoveravano cervelli di prima grandezza e i risultati furono il Fortran, che a tutt’oggi è il punto di riferimento per il calcolo numerico e il Lisp, che a tutt’oggi è il punto di riferimento per quanto riguarda le tecniche di metaprogrammazione. Entrambi questi linguaggi sono stati e sono ancora di enorme successo nei rispettivi ambiti di applicazione, e probabilmente saranno usati per almeno un altro secolo con altrettanto successo.

Ciò nonostante, sia il Fortran che in Lisp sono oggigiorno dei linguaggi di scarsa visibilità, che occupano delle nicche importanti ma molto limitate nell’ambito di ciò che si intende per programmazione oggi.

Il motivo della scarsa popolarità del Fortran è chiaro: il linguaggio è stato disegnato con uno ed un solo scopo in mente, l’efficienza nel calcolo numerico (il cosiddetto “number crunching”). Per tutto il resto il Fortran non è una scelta molto appetibile. Oggi come oggi, una grandissima quantità di programmatori non ha affatto bisogno di scrivere routines per il calcolo di numeri in virgola mobile, quindi non c’è nessun bisogno di usare il Fortran. Anche per chi si occupa di “number crunching” il C/C++ è quasi altrettanto efficiente ed ha dei notevoli vantaggi dal punto di vista dell’interfaccia con il sistema operativo (se poi l’efficienza non conta proprio per nulla, uno può sempre usare Python ;).

Il motivo della scarsa popolarità del Lisp è meno chiaro: potenzialmente il Lisp potrebbe fare qualunque cosa, è quasi altrettanto efficiente del C, ma quasi nessuno lo usa. I newsgroups rigurgitano di messaggi infuocati che descrivono da una parte i vari motivi per cui il Lisp è morto e dall’altra parte i motivi per cui invece non è affatto morto ed anzi sarà il linguaggio del futuro. Non è certo mia intenzione invischiarmi in simili polemiche e quindi mi asterrò prudentemente dal formulare ogni opinione sui motivi della scarsa visibilità del Lisp, tanto più che onde evitare ogni pericolo non parlerò affatto del Lisp in questi articoli, parlerò invece di Scheme ;)

Del linguaggio algoritmico Scheme

Scheme Shield

Scheme nasce nel 1975 (è quindi quasi due volte più vecchio di Python) come un dialetto del Lisp. Per chi non lo sapesse il Lisp, più che un linguaggio, è una famiglia di dialetti. Oggi con il termine “Lisp” si intende comunemente il linguaggio “Common Lisp” standardizzato nel 1989 e quindi posteriore a Scheme. Discutere le differenze e i rispettivi vantaggi di Scheme e (Common) Lisp sarebbe lungo e mi porterebbe su di un suolo minato: basti dire che molti Schemers e Lispers si odiano cordialmente e pensano che i loro linguaggi siano diversissimi l’uno dall’altro, mentre chiunque non conoscesse nè il primo nè il secondo avrebbe difficoltà a distinguerli(!)

Di fatto i due linguaggi condividono una quantità di aspetti e molto di quanto dirò a proposito di Scheme si applica anche al Lisp. Le differenze maggiori sono filosofiche e sociali: la comunità del Lisp è più interessata ad avere un linguaggio completo e usabile nel mondo reale; la comunità di Scheme invece è più interessata ad avere un linguaggio piccolo, adatto alla didattica (Scheme è usato in molte università come primo linguaggio di programmazione) e alla ricerca nel mondo accademico.

Naturalmente questa è una grossa semplificazione poiché vi sono implementazioni di Scheme piuttosto complete che possono senza dubbio competere con le implementazioni del Lisp. In ogni caso, sia per quanto riguarda Scheme che Lisp, quando si parla di “completezza di un’implementazione” non aspettatevi nulla di simile a Python: il supporto per quanto riguarda librerie esterne ed affidabilità è di gran lunga inferiore, soprattutto nel caso di Scheme.

Storicamente, sia Scheme che il Common Lisp nascono dal mare magnum dei dialetti del Lisp: tuttavia mentre lo standard del Common Lisp nasce come unione delle caratteristiche presenti nei vari dialetti, lo standard di Scheme nasce come intersezione delle caratteristiche presenti nei vari dialetti. Come recita il Revised Report 5 on the Algorithmic language Scheme , lo standard “corrente” di Scheme, comunemente chiamato R5RS:

Programming languages should be designed not by piling feature on top of feature, but by removing the weaknesses and restrictions that make additional features appear necessary. — William Clinger

Come conseguenza di questo principio lo standard R5RS di Scheme è molto più breve di quello del Common Lisp: di fatto troppo breve, con la conseguenza che non è possibile scrivere applicazioni “reali” aderendo strettamente allo standard.

A questa situazione sfortunata si è cercato di rimediare recentemente, con l’introduzione di un nuovo standard, molto più esteso del precedente, il famigerato R6RS ( Revised Report 6 on the Algorithmic language Scheme). Dico “famigerato” perché la gestazione del nuovo standard è stata travagliatissima: una parte significativa della comunità ha visto il nuovo standard come un tradimento dei principi guida di Scheme.

L’R5RS era uno standard ridotto pensato per rendere Scheme un linguaggio semplicissimo da implementare. Di fatto, qualunque studente di Informatica un po’ sveglio si poteva mettere a tavolino e scrivere la sua implementazione di Scheme senza troppi problemi (esistono anche implementazioni di Scheme scritte in Python, se vi interessa). Dopo l’avvento dell’R6RS la barra per scrivere un’implementazione di Scheme compatibile con lo standard si è alzata di molto: occorre implementare un sistema di moduli, un sistema di condizioni simile a quello del Common Lisp, macro avanzate, una libreria standard, garantire il supporto a Unicode e molto altro che prima non era necessario.

Non solo è più difficile scrivere una implementazione compatibile, ma è anche piuttosto difficile prendere una implementazione esistente compatibile con il vecchio R5RS ed aggiornarla al nuovo standard. Siccome l’R6RS è così recente (è stato pubblicato nel settembre 2007) non esistono ancora molte implementazioni conformi; nel momento in cui scrivo sono apparse Larceny ed Ikarus; io userò Ikarus per i miei esempi.

Logo di Ikarus Scheme

L’installazione è banale, basta scaricare la tarball, compilare con ./configure && make && sudo make install e siete a posto:

$ ikarus
Ikarus Scheme version 0.0.2
Copyright (c) 2006-2007 Abdulaziz Ghuloum

> (display "hello world\n")
hello world

Se state usando Windows, vi conviene installare Common Larceny, che gira nell’ambiente .NET.

Con questo chiudo questa puntata: vi rimando alla prossima per una discussione sulle implementazioni di Scheme e sul problema delle librerie. Arrivederci e a presto!

Comments

  1. Bell’articolo.
    Un’unico appunto “storico”: scheme non e’ nato come intersezione dei lisp precedenti, ma come studio sugli “actors”, per questo ha introdotto lexical scoping (in un primo considerato un “tradimento”, poi adottato dal common lisp) e continuations.
    L’enfasi sul minimalismo, e la frase che hai citato di Clinger risalgono a R3RS, se non sbaglio.

  2. Articolo che mi ha incuriosito davvero molto, non puoi chiudere così la storiella! Non vedo l’ora di vedere i primi esempi in Scheme, che veramente in questo momento mi sembra identico a LISP….

    P.S.
    Non conoscevo lo stemma del MIT in lambda calculus, ma è vero??

  3. unwiredbrain says:

    Ma come!!? Prima metti tanta carne al fuoco e poi mi spegni le braci??

    Uffa :)

  4. Michele Simionato says:

    Eh, eh ;) Pazientate gente, pazientate!
    Nelle mie intenzioni questa e’ una serie di ampio respiro che ci accompagnera’ per molte puntate.

  5. Ho dovuto studiare lisp per il corso di paradigmi di programmazione e devo ammettere che ,nonstante la titubanza iniziale dovuta anche alla logica funzionale, solo dopo aver realizzatto un progetto mi sono reso conto delle sue potenzialità. A mio parere Lisp e Scheme hanno ancora molto da dire, fosse solo anche a livello di formazione mentale!

  6. Se dovessi raccontare a qualcuno che sono rimasto con la suspence alla fine di un articolo su Scheme, credo mi prenderebbe per pazzo :P

    Attendiamo la prossima puntata ;)

  7. Nessuno ha ancora citato il bellissimo “Structure and interpretation of computer programs”, detto anche “Wizard Book”. Un testo secondo me fondamentale, non solo se si è interessati al linguaggio Scheme. Non penso di aver suggerito niente di nuovo, ma forse sono meno noti i video delle lezioni tenute da Abelson e Sussman: http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/

  8. larsen: e pensare che il corso 6.001 è stato tolto dal programma!

  9. quoto in pieno simbul!
    fiato sospeso fino alla fine e ora grande attesa per la seconda puntata .. ma non ditelo in giro, mi internerebbero!

  10. @riffraff: non lo sapevo. mi sembra un peccato

Policy per i commenti: Apprezzo moltissimo i vostri commenti, critiche incluse. Per evitare spam e troll, e far rimanere il discorso civile, i commenti sono moderati e prontamente approvati poco dopo il loro invio.