Pourquoi la communauté Lisp préfère-t-elle accumuler toutes les parenthèses à la fin de la fonction?

26

Pourquoi la communauté Lisp préfère-t-elle accumuler toutes les parenthèses à la fin de la fonction:

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

Pourquoi ne pas utiliser une convention comme C ou Java?
Bon ok, Lisp est beaucoup plus ancien que ces langages, mais je parle des Lispers contemporains.

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

Remarque: L'extrait de code provient du livre "The Joy of Clojure".

Chiron
la source
13
On me dit qu'à l'époque des cartes perforées, il était courant de mettre fin à un programme Lisp avec une carte supplémentaire avec 80 parenthèses à droite, juste pour s'assurer qu'il y en avait assez pour correspondre à toutes les parenthèses ouvertes du programme .
Alger
2
Ah, j'aime bien ça. J'ai toujours détesté les parenthèses, mais votre deuxième exemple me semble agréable. Pas aussi sympa que Python, mais amélioration.
Eric Wilson
5
parce qu'ils le peuvent?
Muad'Dib
Ce serait bien si un dialecte de Clojure faisait de l'indentation une structure de code. Cela supprimerait complètement les parenthèses (comme F #, etc.). Bien sûr, la parenthèse serait toujours légale, si vous le souhaitez.
intrepidis

Réponses:

28

L'une des raisons pour lesquelles les langues basées sur l'algol encouragent les accolades sur leur propre ligne est d'encourager l'ajout de lignes entre les accolades de délimitation sans avoir à déplacer les accolades. Autrement dit, si l'on commence par

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

il est facile de venir et d'ajouter une autre déclaration entre les accolades:

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

Si le formulaire d'origine avait été

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

il faudrait alors avoir "déplacé" deux accolades, mais mon exemple concerne plus ce dernier. Ici, les accolades délimitent ce qui est censé être une séquence d'instructions , principalement invoquées pour un effet secondaire.

Inversement, Lisp manque de déclarations; chaque forme est une expression , donnant une certaine valeur - même si dans de rares cas (en pensant au Common Lisp), cette valeur est délibérément choisie pour être «sans valeur» via un (values)formulaire vide . Il est moins courant de trouver des séquences d'expressions , par opposition aux expressions imbriquées . Le désir «d'ouvrir une séquence d'étapes jusqu'au délimiteur de fermeture» ne se manifeste pas aussi souvent, car au fur et à mesure que les déclarations disparaissent et que les valeurs de retour deviennent une monnaie plus courante, il est plus rare d'ignorer la valeur de retour d'une expression, et donc plus rare d'évaluer une séquence d'expressions pour un effet secondaire seul.

En Common Lisp, le prognformulaire est une exception (tout comme ses frères et sœurs):

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

Ici, prognévalue les trois expressions dans l'ordre, mais ignore les valeurs de retour des deux premières. Vous pouvez imaginer écrire cette dernière parenthèse fermante sur sa propre ligne, mais notez à nouveau que puisque la dernière forme est spéciale ici (pas dans le sens Common Lisp d'être spéciale , cependant), avec un traitement distinct, il est plus probable que l'on ajoute de nouvelles expressions au milieu de la séquence, plutôt que de simplement «en ajouter une autre à la fin», car les appelants seraient alors affectés non seulement par de nouveaux effets secondaires, mais plutôt par un changement probable de la valeur de retour.

Pour faire une simplification grossière, les parenthèses dans la plupart des parties d'un programme Lisp délimitent les arguments passés aux fonctions - tout comme dans les langages de type C - et ne délimitent pas les blocs d'instructions. Pour les mêmes raisons, nous avons tendance à garder les parenthèses délimitant un appel de fonction en C autour des arguments, nous faisons de même de même en Lisp, avec moins de motivation pour dévier de ce regroupement proche.

La fermeture des parenthèses est beaucoup moins importante que l'indentation du formulaire où elles s'ouvrent. Avec le temps, on apprend à ignorer les parenthèses et à écrire et lire par forme, un peu comme le font les programmeurs Python. Cependant, ne laissez pas cette analogie vous faire penser que la suppression complète des parenthèses serait utile. Non, c'est un débat qu'il vaut mieux conserver comp.lang.lisp.

seh
la source
2
je pense que l'ajout à la fin n'est pas si rare, par exemple (let ((var1 expr1) more-bindings-to-be-added) ...), ou(list element1 more-elements-to-be-added)
Alexey
14

Parce que ça n'aide pas. Nous utilisons l'indentation pour montrer la structure du code. Si nous voulons séparer des blocs de code, nous utilisons des lignes vraiment vides.

Étant donné que la syntaxe Lisp est si cohérente, les parenthèses sont le guide définitif de l'indentation pour le programmeur et l'éditeur.

(Pour moi, la question est plutôt de savoir pourquoi les programmeurs C et Java aiment contourner leurs accolades.)

Juste pour démontrer, en supposant que ces opérateurs étaient disponibles dans un langage de type C:

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

Deux accolades fermantes ferment deux niveaux d'imbrication. La syntaxe est évidemment correcte. Par analogie avec la syntaxe Python, les accolades ne sont que des jetons INDENT et DEDENT explicites.

Bien sûr, ce n'est peut-être pas le "seul vrai style d'accolade" TM , mais je crois que ce n'est qu'un accident et une habitude historiques.

Svante
la source
2
En outre, presque tous les éditeurs de Lisp marquent la parenthèse correspondante lors de la fermeture (certains aussi correct )à ]ou vice - versa), vous savez que vous avez fermé la bonne quantité , même si vous ne cochez pas les niveaux d'indentation.
configurateur
6
WRT "la question", parce que "jeter" les jetons de fermeture dans le style du deuxième exemple vous permet de les aligner facilement avec vos yeux et de voir ce qui ferme quoi, même si vous êtes juste dans un éditeur de texte sans correspondance automatique / mise en évidence des fonctionnalités.
Mason Wheeler
1
@Mason Wheeler Oui, exactement.
Chiron
3
TBH, ce que cela me dit, c'est que les parens dans LISP sont pour la plupart redondants, avec la possibilité (comme avec C ++) que l'indentation puisse induire en erreur - si l'indentation vous dit ce que vous devez savoir, cela devrait aussi dire au compilateur la même chose, comme dans Haskell et Python. BTW - avoir les accolades au même niveau de retrait que le if / switch / while / tout en C ++ permet d'éviter les cas où l'indentation est trompeuse - un balayage visuel dans le LHS vous indique que chaque accolade ouverte est mise en correspondance avec une accolade rapprochée et cette indentation est compatible avec ces accolades.
Steve314
1
@ Steve314: Non, l'indentation est redondante. L'indentation peut aussi induire en erreur en Python et Haskell (pensez à l'indentation unique ou aux tableaux), c'est juste que l'analyseur est également trompeur. Je pense que les parenthèses sont un outil pour l'écrivain et l'analyseur, tandis que l'indentation est un outil pour l'écrivain et le lecteur (humain). En d'autres termes, l'indentation est un commentaire, les parenthèses sont la syntaxe.
Svante
11

Le code est alors beaucoup plus compact. Le mouvement dans l'éditeur se fait de toute façon par des expressions s, donc vous n'avez pas besoin de cet espace pour l'édition. Le code est lu principalement par la structure et les phrases - pas par les délimiteurs suivants.

Rainer Joswig
la source
Quel honneur d'avoir une réponse de votre part :) Merci.
Chiron
Quel éditeur se déplace par s-expressions? Plus précisément, comment puis-je vimfaire cela?
hasen
2
@HasenJ Vous pourriez avoir besoin du vimacs.vim plugin . : P
Mark C
5

Lispers, vous savez, aussi haineux que cela puisse être, il y a un certain je ne sais quoi dans le deuxième exemple:

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

Au début, je ne pouvais pas mettre le doigt sur la source de l'attrait, pour ainsi dire, puis j'ai réalisé qu'il manquait juste un déclencheur:

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

Voilà!

Je vais pratiquer mon grip gangster Lisp maintenant!

Kaz
la source
2
C'est l'une des réponses les plus drôles et les plus sous-estimées que j'ai vues sur ce site. Je penche mon chapeau.
2017
0

Dans mon cas, je trouve que les lignes dédiées aux délimiteurs sont un gaspillage d'espace à l'écran, et lorsque vous écrivez du code en C, il y a aussi le style de

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

Pourquoi les gens mettent-ils l'accolade ouvrante dans la même ligne du "si" pour économiser de l'espace, et parce qu'il semble redondant d'avoir sa propre ligne alors que le "si" a déjà sa propre ligne.

Vous atteignez le même résultat en rassemblant les parenthèses à la fin. En C, cela aurait l'air bizarre car les instructions se terminent par un point-virgule et la paire de crochets ne s'ouvre pas comme ceci

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

il est comme cette accolade de fermeture à la fin, semble hors de propos. ça s'ouvre comme ça

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

vous donnant une vue claire du bloc et de ses limites par '{' & '}'

Et avec l'aide d'un éditeur qui correspond aux parenthèses en les mettant en surbrillance comme le fait vim, vous pouvez aller à la fin où toutes les parenthèses sont compressées et les parcourir facilement et faire correspondre toutes les parenthèses d'ouverture et voir la forme lisp imbriquée

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

vous pouvez placer votre curseur dans la paren de fermeture au milieu et avoir la paren d'ouverture de l'if-let en surbrillance.

kisai
la source