il blog di dalz

chi sonogeminienglishicona di feed RSS

Oggetti strutturati tramite chiusure lessicali
2021-07-15

Una delle idee più interessanti che ho trovato fin’ora leggendo SICP è l’implementazione di oggetti da “zero”. Ad esempio, la classica cella cons dei Lisp può essere costruita così:

(define (cons x y)
  (lambda (f) (f x y)))

(define (car z)
  (z (lambda (x y) x)))

(define (cdr z)
  (z (lambda (x y) y)))

Per chi non digerisce le parentesi ecco l’equivalente in Python:

def pair(x, y):
    return lambda f: f(x, y)

def first(z):
    return z(lambda x, y: x)

def second(z):
    return z(lambda x, y: y)

p = pair(4, 2)
second(p)
# => 2

In questo esempio si definisce una coppia come una funzione che presa in input un’altra funzione la applica ai due elementi della coppia, che vengono memorizzati dal meccanismo delle chiusure lessicali.

Possiamo anche creare strutture con più campi:

(define (make-user name phone address)
  (lambda (n)
    (case n
      ((0) name)
      ((1) phone)
      ((2) address))))

(define (user-name u) (u 0))
(define (user-phone u) (u 1))
(define (user-address u) (u 2))

Per poi usarle così:

(define pippo (make-user "Pippo" "12467620823" "Via Roma"))
(user-name pippo)
;; => "Pippo"

In questo caso abbiamo usato un approccio forse concettualmente più semplice, l’oggetto è una funzione che prende in input un numero e restituisce il valore del campo corrispondente.

Tutto ciò ci permette di creare semplici strutture immutabili, ma se volessimo di più?

(define (make-account)
  (define balance 0)

  (lambda (msg)
    (case msg
      ((balance) balance)

      ((deposit)
       (lambda (amount)
         (set! balance (+ balance amount))))

      ((withdraw)
       (lambda (amount)
         (if (> amount balance)
           (error "insufficient balance")
           (set! balance (- balance amount)))))

      (else (error "undefined property or method" msg)))))

Ora il risultato di (make-account) è una procedura che riceve in input un simbolo e restituisce o il bilancio del conto bancario, oppure le operazioni di deposito e prelievo (a loro volta procedure). Anche adesso la proprietà balance viene memorizzata tramite chiusure lessicali. L’utilizzo è il seguente:

(define acc (make-account))

(acc 'balance)
;; => 0

((acc 'deposit) 100)
(acc 'balance)
;; => 100

Il modo in cui interagiamo con l’oggetto, ovvero passandogli un messaggio e utilizzando la risposta, dà il nome di message passing a questa tecnica di implementazione.

Possiamo naturalmente semplificare l’utilizzo dell’oggetto nascondendo il messaggio in procedure apposite:

(define (account-balance account)
  (account 'balance))

(define (account-deposit account amount)
  ((account 'deposit) amount))

(define (account-withdraw account amount)
  ((account 'withdraw) amount))

(account-withdraw acc 25)
(account-balance acc)
;; => 75

È veramente affascinante quello che si può fare avendo a disposizione un modo per definire funzioni e poco altro!



Domande, dubbi, incertezze, carenze affettive? Manda tutto a blog@alsd.eu!
Torna alla lista degli articoli.