Perché la comunità Lisp preferisce accumulare tutte le parentesi alla fine della funzione?

25

Perché la comunità Lisp preferisce accumulare tutte le parentesi alla fine della funzione:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)))

Perché non utilizzare una convenzione come C o Java?
Bene, il Lisp è molto più vecchio di quelle lingue, ma sto parlando dei Lispers contemporanei.

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

Nota: lo snippet di codice è tratto dal libro "The Joy of Clojure".

    
posta Chiron 07.08.2011 - 18:01
fonte

4 risposte

26

Un motivo per cui le lingue basate su Algol incoraggiano le parentesi sulla propria linea è di incoraggiare l'aggiunta di più linee tra le parentesi delimitanti senza dover spostare le parentesi. Cioè, se uno inizia con

if (pred)
{
  printf("yes");
}

è facile venire avanti e aggiungere un'altra istruzione tra parentesi:

if (pred)
{
  printf("yes");
  ++yes_votes;
}

La forma originale era stata

if (pred)
{ printf("yes"); }

quindi dovremmo avere "spostato" due parentesi, ma il mio esempio è più interessato a quest'ultimo. Qui, le parentesi stanno delimitando cosa si intende per una sequenza di istruzioni , principalmente invocata per effetto collaterale.

Al contrario, Lisp manca di dichiarazioni; ogni forma è espressione , dando qualche valore, anche se in alcuni rari casi (pensando a Common Lisp), quel valore viene scelto deliberatamente come "nessun valore" tramite un (values) modulo. È meno comune trovare sequenze di espressioni , al contrario di espressioni nidificate . Il desiderio di "aprire una sequenza di passaggi fino al delimitatore di chiusura" non si pone più spesso, perché quando le dichiarazioni scompaiono e i valori restituiti diventano una valuta più comune, è più raro ignorare il valore di ritorno di un'espressione, e quindi più raro valutare una sequenza di espressioni solo per effetto collaterale.

In Common Lisp, il modulo progn è un'eccezione (come lo sono i suoi fratelli ):

(progn
  (exp-ignored-return-1)
  (exp-ignored-return-2)
  (exp-taken-return))

Qui, progn valuta le tre espressioni in ordine, ma scarta i valori di ritorno dei primi due. Potresti immaginare di scrivere quell'ultima parentesi chiusa sulla propria linea, ma nota nuovamente che poiché l'ultima forma è speciale qui (non nel senso comune di Lisp di essere speciale , però), con un trattamento distinto, è più probabile che si aggiungano nuove espressioni nel mezzo della sequenza, piuttosto che solo "aggiungendo un altro alla fine", poiché i chiamanti sarebbero quindi interessati non solo da nuovi effetti collaterali, ma piuttosto da un probabile cambiamento nel valore di ritorno.

Facendo una semplificazione grossolana, le parentesi nella maggior parte delle parti di un programma Lisp stanno delimitando gli argomenti passati alle funzioni, proprio come nei linguaggi di tipo C, e non che delimitano i blocchi di istruzioni. Per gli stessi motivi tendiamo a mantenere le parentesi che delimitano una chiamata di funzione in C vicino agli argomenti, così facciamo lo stesso in Lisp, con meno motivazione a deviare da quel raggruppamento chiuso.

La chiusura delle parentesi è molto meno importata della rientranza del modulo in cui si aprono. Col tempo, si impara a ignorare le parentesi e si scrive e si legge per forma, proprio come fanno i programmatori Python. Tuttavia, non lasciare che questa analogia ti porti a pensare che rimuovere le parentesi sarebbe valso la pena. No, si tratta di una discussione salvata per comp.lang.lisp .

    
risposta data 08.08.2011 - 01:33
fonte
13

Perché non aiuta. Usiamo indentazione per mostrare la struttura del codice. Se vogliamo separare i blocchi di codice, usiamo linee veramente vuote.

Poiché la sintassi Lisp è così coerente, le parentesi sono la guida definitiva all'indentazione sia per il programmatore che per l'editor.

(Per me, la domanda è piuttosto il motivo per cui i programmatori di C e Java amano aggirare le loro parentesi.)

Solo per dimostrare, supponendo che questi operatori fossero disponibili in un linguaggio simile a C:

Foo defer_expensive (Thunk cheap, Thunk expensive) {
    if (Foo good_enough = force (cheap)) {
        return good_enough; }
    else {
        return force (expensive); }}

Due parentesi graffe chiudono due livelli di nidificazione. La sintassi è ovviamente corretta. In analogia con la sintassi di Python, le parentesi graffe sono solo token INDENT e DEDENT espliciti.

Naturalmente, questo potrebbe non essere il "one true brace style" TM , ma credo che sia solo un incidente storico e abitudine.

    
risposta data 07.08.2011 - 18:13
fonte
11

Il codice è molto più compatto allora. Il movimento nell'editor è comunque per le espressioni S, quindi non è necessario lo spazio per il montaggio. Il codice viene letto principalmente dalla struttura e dalle frasi, non seguendo i delimitatori.

    
risposta data 18.08.2011 - 20:00
fonte
4

Lispers, sai, odiosamente blerzioso, è un certo je ne sais quoi al secondo esempio:

(defn defer-expensive [cheap expensive]
  (if-let [good-enough (force cheap)]
    good-enough
    (force expensive)
  )
)

All'inizio non riuscivo a mettere un dito sulla fonte del fascino, per così dire, e poi ho capito che mancava semplicemente un trigger:

(defn defer-expensive [cheap expensive]      
  (if-let [good-enough (force cheap)]
    good-enough   ;   )
    (force expensive) 
  )
)

Voilà!

Ora mi eserciterò nella mia presa del gangster Lisp!

    
risposta data 16.12.2015 - 03:16
fonte

Leggi altre domande sui tag