Archivi Categorie: Clojure

Clojure, concetti fondamentali I

Rich Hickey

Rich Hickey

Mi sono letto un bel po’ di manuali, è ora di provare. Con una premessa: non so come va a finire, è un esperimento. E poi non è legato a un progetto futuro, non dovrò usarlo (almeno non è atualmente previsto). Mi interessa approfondire il Lisp, in una delle sue più recenti versioni. E poi c’è la JVM, Java Virtual Machine, non sono un aficionado di Java ma JVM è una grande idea.

Insomma, seguendo Clojure Programming di Chas Emerick, Brian Carper, and Christophe Grand provo a smanettare un pochino, riportando qui i miei tentativi. Chissà…

Allora provo a partire con i concetti fondamentali di Clojure. Che sono parecchi, ogni varietà di Lisp è diversa da tutte le altre, alcune volte solo un tantino ma altre molto di più. Poi io ho cominciato all’inizio degli anni ’80 e sono fermo lì (non è vero ma è una frase che fa effetto).

Parto con un esempio semplice, la media di una serie di numeri

(defn average [numbers]
    (/ (apply + numbers) (count numbers)))

Definire la funzione è semplicissimo, uso defn (ne riparlerò in seguito) seguito dal nome che voglio dare alla funzione, average in questo caso. Segue il vettore dei dati che passo alla funzione. Qui rispetto al Lisp di una volta c’è la prima (quasi) sorpresa (per me, lo so, so so!): le parentesi quadre; anche il vettore al posto di una lista è una novità. Risulta in ogni caso più leggibile, ottimo.
Sulla riga successiva c’è tutto il corpo (body) della funzione nonché la parentesi che chiude la definizione di funzione. In realtà la distribuzione dello script tra le varie righe è completamente arbitraria, da sempre nel Lisp.
Per quanto riguarda il codice è elementare:
(apply + numbers) è la lista che usa apply per applicare la funzione + agli elementi di numbers e ne ritorna la somma; (count numbers) restituisce il numero di elementi di numbers; avendo somma e numero dei dati basta applicare la funzione / e abbiamo la media.

Nell’ambiente interattivo (il REPL, read, eval, print, loop) risulata

a0

Posso procedere anche nel solito modo per l’esecuzione dello script:

#!/usr/bin/env clojure
; average-n.clj

(defn average [numbers]
    (/ (apply + numbers) (count numbers)))

(println (average [60 80 100 400]))

a1

Dai, è la prima volta, ecco l’equivalente in Python:

#!/usr/bin/python
# -*- coding: utf-8 -*-

def average (numbers):
	return sum(numbers) / len(numbers)

numbers = [60, 80, 100, 400]
print average(numbers)

OPS! sono decisamente simili! C’è infatti chi ha definito Python un Lisp con sintassi normale 😉

Espressioni, operatori, sintassi e precedenza

In Clojure il codice è costituito da espressioni, ognuna delle quali ritorna un valore. Questo è in contrasto con i linguaggi normali che hanno istruzioni (o blocchi di istruzioni) senza valori (come if, for, continue) per controllare il flusso del programma.
Vediamo qualche espressione:

60
[60 80 100 400]
(average [60 80 100 400])
(+ 1 2)

queste espressioni ritornano tutte un valore singolo, con regole molto semplici:

  • liste, denotate da () sono chiamate dove il primo valore nella lista è l’operatore che viene applicato al resto della lista;
  • simboli (come average o +) sono il corrispondente del nome di funzione nei linguaggi normali;
  • tutte le altre espressioni hanno il valore letterale di quel che descrivono.

L’uso delle s-espressions (s sta per symbolic) delle liste dove il primo elemento è “la funzione” cui applicare gli elementi successivi risolve il problema delle precedenze: problema che non c’è.

Omoiconicità (Homoiconicity)

Ecco una parola difficile messa lì per dire code-as-data, il codice e i dati sono indistinguibili. Non credo interessi tutto lo spiegone teorico; per quanto mi riguarda mi basta che questo porti alle macro, la forza del Lisp.

Scalar Literals
Credo siano l’equivalente dei tipi base degli altri linguaggi.

Stringhe
Sono istanze a java.lang.Strings:

s0

ma, come Python, possono essere multilinea:

s1

Booleani
Si usano true e false. nil corrisponde a null in Java e None in Python; è logicamente falso.

Caratteri
i caratteri vengono rappresentati con un backslash:

c

e c’è l’Unicode (e non solo):

c1

poi ci sono quelli con i nomi speciali \space, \newline, \formfeed, \return, \backspace e \tab.

Keywords
evaluate to themselves“, come si traduce? sono valutate per se stesse. Sono usate frequentemente, per esempio in questa mappa (map):

k

Questa è un’hashmap con due slot :name e :city. Notare che le keyword sono funzioni relative alla collezione.
Il nome della keyword deve iniziare con : due-punti. Per indicare il namespace si usa / (nel caso sopra è user, quello di default). UN doppio due-punti (::) viene espanso a una keyword con namespace nel namespace corrente, esempio (che altrimenti non si capisce):

pizza

Simboli
Sono identificatori come le keyword ma hanno il valore loro assegnato a runtime. Tornando al primo esempio:

(average [60 80 100 400])

average qui è un simbolo riferentesi alla funzione nella var chiamata average. I simboli hanno un nome che deve iniziare con un carattere non numerico seguito da qualsiasi sequenza di caratteri alfanumerici, possono esserci inoltre *, +, !, -, _, e ?. Nel caso si riferisca a un particolare namespace c’è il solito /.

Ecco, siccome è ancora lungo per oggi basta così 😉

Poi me lo spieghi. Hai usato una libreria?

Poi me lo spieghi. Hai usato una libreria?

Continua, prossimamente 😀

Clojure – forse

clojure-icon

Non so se questo avrà un seguito. Per adesso è una prova, ho letto un paio di tutorial e mi sembra fattibile, o quanto meno non impossibile.
Soddisferebbe anche la mia voglia di qualcosa di diverso dal solito, e poi c’è la programmazione funzionale, quella che con Haskell mi sembra sempre che mi manchi qualcosa per poter partire. E poi il Lisp, io mi sono innamorato del Lisp almeno tre volte, la prima prima ancora di DOS|Windows, la seconda con Windows e la terza con Linux. Tranne la prima c’è di mezzo newLISP (ultimamente molto cambiato, sexy, mi attira) ma sembra che adesso vada di moda Clojure.

Tutto questo non ha scopi pratici, diciamo che dev’essere divertente farlo, imparare una cosa nuova come quando ero a scuola.
E mi stuzzica l’interazione con JVM. JVM è una delle grandi idee degli anni ’90, una di quelle che hanno cambiato il mondo.

Adesso vi racconto un fatto capitato nel ’95, da Giors (poi gli chiedo se se lo ricorda anche lui). Giors aveva un libricino (allora il Web era ancora agli inizi e le cosa giravano ancora su carta) nuovo di pakka, su Java che era nuovissimissimo. Capitiamo da lui io e Ubaldo; Ubaldo prende il libretto (allora i linguaggi erano umani e i manuali abbordabili, ricordate il K&R?) lo sfoglia, vede le parentesi graffe e prima di subito chiede il permesso di fotocopiarlo, l’ottiene e si mette a codificare (dopo aver avuto l’attenzione di tutti e l’accesso a un PC). In un nientesimo di secondo aveva fatto qualche classe e parecchie subclassi e ottenuto il suo primo applet (credo qualcosa che scriveva “Fatto da Ubaldo” sul terminale e nel file Ubaldo.txt).
Ecco, 18 anni dopo tocca a me, forse 😉

Clojure con Ubuntu si installa dal Software Center, fatto senza problemi.

f0

OK, c’è vediamo se …

f10

OK, e poi …

f1

Sì, come Ubaldo (quasi). Ma per questo c’è tempo, devo documentarmi. E poi io sono abituato a fare un passo per volta:

It’s a long way to Ankh-Morpork, it’s a long way to go!
(eh, la metrica, doh!) 😉