Le avventure di un Pythonista in Schemeland /2

Scheme è un linguaggio con molte implementazioni e con poche librerie.
In questa puntata discuterò un pò la situazione corrente e darò qualche
consiglio utile al programmatore Scheme principiante.

Delle implementazioni di Scheme

Uno dei problemi più grandi per chi inizia a studiare Scheme è
scegliere da quale implementazione partire: io ci ho perso dei mesi e
più di una volta sono stato sul punto di lasciar perdere tutto. Il fatto
è che ogni volta che trovavo un bug oppure un problema di installazione
mi veniva voglia di cambiare completamente implementazione. D’altra
parte le implementazioni sono piuttosto diverse tra di loro e
sostanzialmente incompatibili, quindi se fate la scelta "sbagliata" c’è
da penare per riconvertire tutto il vostro codice (la cosa è vera anche
al giorno d’oggi, perché non è affatto ovvio che tutte le implementazioni
decidano di uniformarsi al sistema di moduli descritto dall’R6RS).

A questo punto suppongo che mi chiederete quale sia la scelta giusta.
La risposta è che non c’è una scelta giusta: dipende da quello
che vi serve. Ogni implementazione fornisce vantaggi differenti:
per esempio è possibile sviluppare in una certa implementazione interpretata
e compilare successivamente in una seconda implementazione compilata
per avere vantaggi di efficienza. Naturalmente questo va fatto con
accortezza, per i problemi di compatibilità tra diverse
implementazioni di cui parlavo prima. Ci sono implementazioni
che hanno un’interfaccia con il C molto semplice, altre che
sono perfettamente integrate con Java, altre che hanno una buona
documentazione, altre ancora che hanno delle librerie particolarmente utili,
ma non esiste una sola implementazione
che ha tutte queste caratteristiche od è decisamente superiore a
tutte le altre.

Io non posso certo dire di avere provato tutte le implementazioni
di Scheme (sono decine e decine) e quindi le mie osservazioni
devono essere prese "cum grano salis". Le implementazioni che
ho provato sono PLT Scheme, Bigloo, Chicken, Guile, Ikarus e Larceny
che hanno la caratteristica
di essere Open Source, multipiattaforma e gratuite. Altre implementazioni
maggiori sono Chez Scheme (gratuito l’interprete, chiamato Petit Scheme,
a pagamento il compilatore) e il MIT Scheme (distribuito con la licenza
GPL), di cui però non posso dire nulla in prima persona.

Tutte le implementazioni che ho provato (tranne Guile) possono essere compilate
e/o generare codice C, quindi di fatto sono molto più veloci di Python.
Bigloo in particolare è un "high performance compiler" particolarmente adatto
per calcoli in virgola mobile.
PLT Scheme comprende un interprete, un compilatore e un ambiente di sviluppo
grafico ed è forse l’implementazione più completa e sofisticata attualmente
in esistenza; Guile è la versione di Scheme della FSF ed è usato come linguaggio di
scripting del GIMP; nelle idee di Richard Stallman le versioni future di Emacs
avrebbero dovuto abbandonare l’emacs lisp in favore di Guile.
Chicken è un’implementazione giovane il cui principale vantaggio è
quella di essere stata scritta da un autore molto simpatico che vi sta
a sentire se trovate dei bugs (a questo punto un saluto a Felix Winkelmann
è d’obbligo 😉 mentre non ho trovato un grande supporto in questo senso
con PLT Scheme. Naturalmente poi voi potete avere esperienze diverse.

Esistono molte altre implementazioni di Scheme
Open Source e gratuite consistenti con lo standard R5RS, per tutte le piattaforme,
compresa .NET e Java. Il consiglio è quello di partire con una
delle implementazioni cosiddette maggiori. Vi avverto
comunque che, a mio modesto parere, anche le implementazioni
maggiori non sono paragonabili a Python per quanto riguarda affidabilità
e professionalità.
Mi riferiscono non soltanto allo scarso supporto per
le librerie di uso comune nel mondo del lavoro (GUI, Web, etc)
ma anche al fatto che ho riscontrato molti più bugs in Scheme
(nelle implementazioni che ho provato) che in Python.

Logo di GUILE

Logo di Guile

Del problema delle librerie in Scheme

Il punto dolente di Scheme sono le librerie: non
c’è paragone tra le librerie disponibili in Python e le librerie
disponibili in Scheme. Limitiamoci anche solo al caso delle librerie
grafiche: in Python è possibile interfacciarsi con le librerie native
della piattaforma che state utilizzando o con toolkit portabili quali
Tk, GTK, Qt, etc. senza nessuna difficoltà. Siete fortunati se
trovate una implementazione di Scheme che supporta decentemente uno
dei toolkit maggiori, non certo tutti. Ci sono implementazioni di
Scheme in cui creare dei wrappers a librerie esterne scritte in C è
molto semplice, più semplice che in Python: tuttavia siete VOI che
dovete scrivere l’interfaccia, mentre in Python c’è qualcuno che ha
già fatto tutto il lavoro sporco da anni e mantiene aggiornata
l’interfaccia senza che a voi costi nulla.

Il problema alla fine risale sempre al numero di utenti: avendo una
comunità divisa in almeno una decina di implementazioni cosiddette maggiori,
ogni implementazione ha un decimo del supporto che meriterebbe. In
parte questo problema si vede anche con il Common Lisp, che pure ha
meno implementazioni di Scheme e in cui passare da un’implementazione
ad un’altra è più semplice: tuttavia i dolori si cominciano a vedere
quando si considerano la portabilità tra diverse architetture, le difficoltà di
installazione e le librerie grafiche. Non c’ alcun dubbio che
linguaggi come Perl (una sola implementazione) e Ruby/Python (più
implementazioni, ma di fatto una sola implementazione di riferimento)
danno infinitamente meno grattacapi, visto che tutta l’attenzione
della comunità è focalizzata in un punto solo e tutti beneficiano
del lavoro di tutti.

La comunità di Scheme ha cercato di rimediare alle deficienze
dell’R5RS_tramite il meccanismo degli SRFI (Scheme Request For
Implementation) che ad un Pythonista non possono che ricordare i PEP
(Python Enhancement Proposal). Chiunque può preparare un SRFI,
ovverossia un documento che descrive una libreria o una serie di
miglioramenti al linguaggio Scheme che si vorrebbero nello
standard. L’idea è che il comitato ufficiale che preparerà il
prossimo Revised Report possa pescare tra gli SRFI delle buone idee da
standardizzare, anche se non c’è alcun obbligo in tale senso. Di
fatto le implementazioni esistenti stanno facendo un certo
sforzo per includere gli SRFI più importanti, cosicché
codice che usa librerie presenti negli SRFI ha maggiori speranze di
essere portabile. Purtroppo quello che è successo con l’R6RS è che
gli editor hanno ignorato bellamente molti degli SRFI esistenti, reinventandoli
in maniera incompatibile e inserendoli nello standard gratuitamente.
Si capisce perché l’R6RS si sia attirato molte critiche.

Ogni SRFI (a differenza dei PEP) deve essere corredato da
una implementazione, giustificando così la "I" finale.
L’implementazione si deve prefiggere la portabilità come
primo requisito, quindi se anche si usa una versione di Scheme che non
supporta un dato SRFI out of the box, è spesso possibile
usare l’SRFI ugualmente, con qualche piccolo ritocco.

Se state usando un’implementazione di R5RS Scheme il mio
consiglio è quello di mettersi a studiare gli SRFI fin dall’inizio,
in quanto è quasi impossibile scrivere qualcosa di interessante in
R5RS Scheme senza sfruttare le librerie fornite dagli SRFI. D’altra
parte, anche se usate un’implementazione di R6RS Scheme molti
SRFI vi torneranno utili lo stesso, quindi vi conviene studiarli in ogni
caso.

Per un Pythonista la "migliore" implementazione di Scheme conforme con l’R5RS
è Chicken (qui chiaramente siamo nel campo delle
opinioni personali, non prendete come oro colato quello che
dico) che è decisamente molto più pratica della media delle implementazioni di Scheme.
Il motivo è che Chicken è un compilatore da Scheme a C in cui è
estremamente semplice scrivere wrappers per librerie C. Grazie anche
a questo Chicken ha tutta una serie di librerie interessanti, i cosiddetti "eggs"
(al momento ce ne sono più di trecento)
che funzionano come gli eggs di Python. È da notare però che Chicken
ha inventato gli eggs anni prima di Python ed ha maggior diritto di
usare il nome! 😉

Chicken Scheme

Logo di Chicken Scheme

Delle ulteriori difficoltà che un Pythonista più aspettarsi

Io ho cominciato a giocare con Scheme nel 2003: a quei tempi,
tutte le implementazioni che ho provato (tranne Guile che è tipicamente
preinstallato in Linux e sotto Cygwin) mi hanno dato grattacapi con
l’installazione: ho installato sotto Red Hat Linux, Mandrake Linux, Cygwin,
Windows 98 prima edizione e seconda edizione e mai una volta che mi
funzionassero al primo colpo! Adesso le cose sono migliorate e di
solito ora riesco ad installare un’implementazione di Scheme in un sistema
Unix al primo colpo, ma tenetevi preparati ad avere qualche grattacapo.
A mio avviso il problema è che anche un’installazione cosiddetta
"maggiore" di Scheme ha al massimo il 2% degli utenti di Python,
cosicché non è stata testata su tutte le piattaforme e
non ha un supporto paragonabile a quello di Python.

Una cosa che probabilmente vi darà problemi è il supporto a GNU readline:
mentre di solito le versioni di Python che si trovano pre-installate sui sistemi
Linux sono "linkate" con la readline, per questioni di licenza la maggior parte
delle implementazioni di Scheme non la includono, a meno di non toccare
i Makefile a mano. La soluzione migliore è quella di usare rlwrap,
che potete installare con apt-get in Debian/Ubuntu o con fink sul Mac.

rlwrap è un’utilissimo programmino che aggiunge supporto per la readline
a qualunque programma con un REPL che non la supporta.
Basta dare rlwrap <scheme-executable>
e non ci sono più problemi ad usare il REPL dal terminale: si ha il controllo
della storia dei comandi utilizzati, il parens matching (quando si chiude una
parentesi viene evidenziata la corrispondente parentesi iniziale), eccetera.
Io sfrutto queste caratteristiche pesantemente e non potrei vivere senza.

A proposito, mi accorgo ora che ho usato il termine REPL (Read-Eval-Print-Loop)
che forse non tutti i miei lettori conoscono: il REPL non è altro che la terminologia
Lisp per quello che in Python è chiamato l’interprete interattivo, l’ambiente di lavoro
con i >>>, tanto per intenderci. Il REPL permette di provare i comandi
interattivamente ed è utilissimo; in Scheme poi è integrato in maniera
eccellente con Emacs (basta utilizzare un buon scheme-mode, quello
di default lascia molto a desiderare, io consiglio quack.el di Neil Van Dyke).
Quello che manca purtroppo è l’equivalente di IPython, ma non c’è
speranza di ottenerlo, perché il linguaggio non ha tutto il supporto
per l’introspezione che ha Python.

In generale (con qualche eccezione) il supporto che si può avere per
quanto riguarda problemi specifici di un’implementazione è decisamente
inferiore al supporto che si può avere con Python; il newsgroup
comp.lang.scheme è amichevole se chiedete come si può implementare
un dato algoritmo, o chiedete delucidazioni su un dato costrutto
particolarmente ostico, ma tenete sempre conto che il numero di
persone che posta laggiù è forse il 5% del numero di persone che posta
su comp.lang.python.

Tutte le implementazioni che ho provato sono di gran lunga inferiori a Python
come semplicità e rapidità di sviluppo: non aspettatevi dei traceback
informativi e neppure dei messaggi di errore troppo intelligenti.
Non aspettatevi neppure
di ottenere il numero di linea in cui si è verificato l’errore: questa è
un’informazione assai difficile da ottenere a causa della struttura stessa
del linguaggio, in cui il codice può essere generato automaticamente dalle macro
e la nozione di numero di linea diventa fumosa.

Per darvi un’idea del problema con i messaggi di errore, potete
confrontare Python con PLT Scheme, che pure è l’implementazione
più completa di Scheme:

Python 2.5.1c1 (r251c1:54694M, Apr  5 2007, 12:45:14)
[GCC 4.0.1 (Apple Computer, Inc. build 5367)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> def inv(x): return 1/x
...
>>> inv(0)
Traceback (most recent call last):
  File "", line 1, in 
  File "", line 1, in inv
ZeroDivisionError: integer division or modulo by zero
Welcome to MzScheme v372 3m, Copyright (c) 2004-2007 PLT Scheme Inc.
> (define (inv x) (/ 1 x))
> (inv 0)
/: division by zero
Logo PLT-Scheme

Logo di PLT-Scheme

Come vedete non c’è traceback, non ci sono numeri di riga, non c’è
neppure il nome della funzione in cui avviene l’errore! A difesa di
PLT Scheme c’è da dire che è pensato per essere utilizzato all’interno
di un ambiente di sviluppo specializzato (DrScheme). DrScheme
vi porta direttamente alla riga in cui c’è l’errore, colorandola, ed offre
anche delle funzionalità di debug. Ma queste funzionalità sono abbastanza
rare nel mondo Scheme, ed in ogni caso secondo la mia personale esperienza
è molto più difficile debuggare un programma in Scheme che un programma
in Python.
Anche il sistema di documentazione non regge il passo con Python:
non c’è nessun analogo di pydoc, niente help in linea, etc.
Il linguaggio non comprende neppure le docstring.

Insomma, la strada da percorrere se volete diventare un programmatore
Scheme è tutta in salita; a livello di tool di sviluppo e di professionalità
delle implementazioni vi conviene utilizzare il Common Lisp.
Comunque, dal punto di vista di chi scrive (senz’altro opinabile) anche
il Common Lisp è di gran lunga meno produttivo di Python per l’uso
comune. Il mio interesse qui non è quello di trovare la "silver bullet",
un linguaggio migliore di Python. Lo scopo è quello di trovare un
linguaggio da cui il Pythonista può impararare qualcosa. E certamente
da Scheme si può imparare molto. Ma questo lo vedremo nelle prossime
puntate. Arrivederci!

Comments

  1. Parlando di librerie scheme, citerei anche snow:

    http://snow.iro.umontreal.ca/

  2. giovanni says:

    Grazie per questo resoconto dettagliato ed istruttivo. Pythonista convinto e professionale, da tempo, periodicamente, vengo preso dal desiderio di avvicinarmi alla famiglia dei linguaggi di ciu parli qui. Con questo articolo mi hai dato informazioni ed opinioni molto utili ad aggiustare le mie aspettative.

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.