Confuso dalla differenza tra let e let * in Scheme

Qualcuno può spiegare la differenza semplicemente? Non penso di aver capito il concetto dai libri di testo / siti che ho consultato.

Se si utilizza let , non è ansible fare riferimento ai collegamenti precedentemente definiti nella stessa espressione let . Ad esempio, questo non funzionerà:

 (let ((x 10) (y (+ x 6))) ; error! unbound identifier in module in: x y) 

Ma se usi let* , è ansible fare riferimento ai binding precedenti nella stessa espressione let* :

 (let* ((x 10) (y (+ x 6))) ; works fine y) => 16 

È tutto qui nella documentazione.

Let è parallelo, (tipo di; vedi sotto) let* è sequenziale. Let tradurre come

 ((lambda(abc) ... body ...) a-value b-value c-value) 

ma let* come

 ((lambda(a) ((lambda(b) ((lambda(c) ... body ...) c-value)) b-value)) a-value) 

e quindi sta creando blocchi di scope annidati in cui l’espressione di b-value può riferirsi a a , e l’espressione di c-value può riferirsi sia a b che a . a-value -app appartiene allo scope esterno. Questo è anche equivalente a

 (let ((a a-value)) (let ((b b-value)) (let ((c c-value)) ... body ... ))) 

Esiste anche letrec , che consente binding ricorsivi, in cui tutte le variabili e le espressioni appartengono a uno scope condiviso e possono riferirsi l’un l’altro (con alcune avvertenze relative all’inizializzazione). È equivalente a

 (let ((a *undefined*) (b *undefined*) (c *undefined*)) (set! a a-value) (set! b b-value) (set! c c-value) ... body ... ) 

( in Racket , disponibile anche come letrec* in Scheme, da R6RS ), o in

 (let ((a *undefined*) (b *undefined*) (c *undefined*)) (let ((_x_ a-value) (_y_ b-value) (_z_ c-value)) ; unique identifiers (set! a _x_) (set! b _y_) (set! c _z_) ... body ... )) 

( in Schema ).

aggiornamento: let realtà non valuta le sue espressioni di valore in parallelo, è solo che sono tutte valutate nello stesso ambiente iniziale in cui appare il modulo let . Ciò è chiaro anche dalla traduzione basata su lambda : prima le espressioni di valore vengono valutate ciascuna nello stesso ambiente esterno , e i valori risultanti vengono raccolti, e solo allora vengono create nuove posizioni per ogni id e i valori vengono inseriti ciascuno nella sua Posizione. Possiamo ancora vedere la sequenzialità se una delle espressioni di valore muta una memoria (cioè dati, come una lista o una struttura) cui si accede da una successiva.