Quand / pourquoi devrais-je utiliser progn?

19

J'ai vu prognbeaucoup utilisé lorsque je parcourais les fichiers de configuration d'utilisateurs expérimentés d'Emacs. J'ai trouvé cette belle explicationprogn , mais ce qui m'intéresse vraiment, c'est quel est l'avantage d'utiliser cette fonction? Prenons par exemple cet extrait (extrait de la configuration de Sacha Chua ):

(use-package undo-tree
  :defer t
  :ensure t
  :diminish undo-tree-mode
  :config
  (progn
    (global-undo-tree-mode)
    (setq undo-tree-visualizer-timestamps t)
    (setq undo-tree-visualizer-diff t)))

Y a-t-il une différence majeure entre la configuration ci-dessus et celle-ci?

(use-package undo-tree
  :defer t
  :ensure t
  :diminish undo-tree-mode
  :config
  (global-undo-tree-mode)
  (setq undo-tree-visualizer-timestamps t)
  (setq undo-tree-visualizer-diff t))

J'ai l'impression que le premier exemple est en quelque sorte plus propre, même s'il a plus de syntaxe, et mon intuition est qu'il pourrait y avoir une sorte d'amélioration des performances progn, mais je ne suis pas sûr. Merci pour tout aperçu!

elethan
la source
7
Dans ce cas particulier, il n'y a aucune différence: use-packageenroulera un prognautour de vos formulaires: config s'il est manquant. Essayez-le: vous pouvez mettre un point à la fin d'un (use-package ...)et appeler M-x pp-macroexpand-last-sexppour voir comment la macro est développée. Vous verrez qu'il est identique pour ces deux exemples.
glucas
Un exemple où cela prognest nécessaire: emacs.stackexchange.com/questions/39172/…
npostavs

Réponses:

11

prognest généralement utilisé pour les macros. Certaines macros ( use-packagec'est une macro, la dernière que j'ai vérifiée) n'acceptent qu'un seul formulaire , tandis que d'autres consomment tous les formulaires restants .

progn est utilisé dans le premier cas pour transformer une séquence de formulaires en un seul formulaire.

Dans vos exemples, le premier utilise prognet donc il y a 1 formulaire après :config. Dans le second, il existe 3 formes . Si la use-packagemacro n'attend qu'un formulaire suivant :config, cela provoquera une erreur.

Il convient de noter que l'utilisation prognfonctionne dans les deux cas, tout en l'omettant ne fonctionne que si la macro accepte plusieurs formulaires. En conséquence, certaines personnes préfèrent simplement toujours utiliser progn, car cela fonctionnera toujours.

J David Smith
la source
15
Cela n'a vraiment rien à voir avec les macros. Certaines fonctions et macros ont un soi-disant "implicite progn", ce qui signifie qu'elles acceptent n'importe quel nombre de sexps comme arguments séparés et les évaluent en séquence. D'autres ne le font pas et s'attendent à la place / n'autorisent qu'un seul argument où vous voudrez peut-être évaluer plusieurs sexps en séquence. C'est tout progn: il vous permet de fournir un seul sexp qui, une fois évalué, évalue les arguments de sexp prognen séquence. Comparez (if true (progn a b c))avec (when true a b c). Dans les deux cas, a, bet csont évalués dans l' ordre.
Drew
4
Je ne suis pas d'accord que 99% des cas d'utilisation concernent des macros, etc. Toute fonction ou macro peut être transmise à un prognsexp contenant plusieurs sexps évalués pour leurs effets secondaires, avant de renvoyer le résultat du dernier de ceux-ci. Cela n'a vraiment rien à voir avec les macros. Les macros "à titre d'exemple" sont très bien. Donner l'impression qu'il prognexiste pour les macros, et que 99% de son utilisation est pour les macros, est trompeur, OMI. prognil s'agit de pelleter une séquence d'évaluations en un seul sexe (pour s'adapter à un argument attendu donné), et il s'agit donc d' effets secondaires .
Drew
5
1. a prognété introduit dans Lisp 1.5 (voir la section La fonction du programme ), en 1962, bien avant l'ajout de macros à Lisp. Le but est d'évaluer séquentiellement les expressions pour leurs effets secondaires. 2. Découvrez progncomment Emacs se présente prognaux programmeurs Elisp. Voir le zap-to-charcode pour un cas d'utilisation simple.
Drew
2
Nous pouvons accepter d'être en désaccord. Mon point est que cela prognn'a rien à voir avec les macros. Son but est bien sûr de " passer plusieurs formulaires comme un seul formulaire " (vos mots) - que ce soit vers une fonction ou une macro ou un formulaire spécial, mais aussi vers " Eval BODY forms séquentiellement et renvoyer la valeur du dernier " (doc string ). Maintenant comme toujours ("ces jours-ci", en effet!). Une évaluation ordonnée n'est importante que pour les effets secondaires, évidemment. Un cas d'utilisation donné (macro ou non) peut ou non se soucier de l'ordre d'évaluation, mais cela n'est pas pertinent. Et Emacs Lisp n'est absolument pas exceptionnel à cet égard.
Drew
10

La raison la plus importante pour progn est décrite dans la première ligne de la documentation progn (accent ajouté):

progn est une forme spéciale qui fait évaluer chacun de ses arguments en séquence et renvoie ensuite la valeur du dernier.

Addenda:

Sans progn , la séquence n'est pas garantie, surtout si les expressions suivantes dépendent des effets secondaires ou des valeurs de retour des expressions précédentes. progn applique la séquence d'exécution de la même manière que la séquence textuelle. Aide à ne pas confondre l'exécution avec l'analyse. Ce comportement remonte aux principes fondamentaux des structures de contrôle lisp et de la programmation fonctionnelle. Voici un extrait (soulignement ajouté) du manuel de référence lisp :

Les structures de contrôle intégrées sont des formulaires spéciaux car leurs sous-formulaires ne sont pas nécessairement évalués ou ne sont pas évalués séquentiellement .

Est- ce que progn augmente les performances?

Analyse des performances, non. Performance d'exécution, non. Au mieux, il peut égaler, mais jamais augmenter les performances comme par magie.

Quand est-ce que progn est utilisé ?

... le plus souvent à l'intérieur d'une protection contre le déroulement, et, ou , ou dans la partie alors d'un if .

Utilisateur Emacs
la source
Je ne sais pas pourquoi c'est si important. Emacs évalue toujours de manière séquentielle.
lunaryorn
2
@lunaryorn voir la réponse mise à jour.
Emacs User
Je ne sais pas si je comprends votre modification. Naturellement, il existe des formulaires spéciaux avec un ordre d'évaluation différent (par exemple if), mais l'ordre d'évaluation normal (par exemple, les formulaires de niveau supérieur, les organes de fonction, etc.) est textuel. Bien sûr, dans une certaine mesure, c'est implicite progn, mais ce n'est généralement pas ce que vous utilisez progn dans votre propre code.
lunaryorn
Je pense que le nœud manuel Elisp (elisp) Sequencingque vous liez en dit long.
Basil
10

Une meilleure façon de comprendre ce qui prognest est de le comparer à la famille: prog1et prog2. Le nou 1ou une 2partie du nom représente l'instruction de la liste dont le résultat vous intéresse. En d'autres termes, prognrenvoie le résultat de la dernière instruction qu'il contient, tandis que prog1renvoie la première, et similaire pour prog2.

Aujourd'hui, cette fonctionnalité semble un peu gênante car nous apprenons à nous attendre à ce que le programme retourne dans la dernière instruction, ou à lui indiquer explicitement ce qu'il doit retourner. Ainsi prog1et prog2sont très rarement utilisés. Cependant, si vous y réfléchissez, cela a du sens, et voici comment:

Différents langages de programmation utilisent différentes stratégies pour décrire leur sémantique. La famille Lisp était étroitement liée à la sémantique dénotationnelle. Sans entrer dans les détails, ce type de sémantique a une difficulté particulière avec quelque chose que nous avons appris à connaître sous le nom d '"énoncés", qui ne sont pas des "expressions". Les significations du code sont généralement pensées en termes de combinaisons de fonctions, tandis qu'une «instruction» qui ne peut même pas être décrite comme une fonction (car elle n'évalue rien) est donc difficile à gérer. Cependant, puisque Lisp autorise les effets secondaires, parfois un programmeur voudrait utiliser une expression sans utiliser sa valeur de manière à ce qu'elle n'affecte pas l'expression qui la suit. Et c'est là que la progXfamille entre en jeu.

Dans les langages de type C, cette fonction est parfois appelée point de séquence (c'est ;-à- dire et ,par exemple). prognest aussi essentiel pour les langages de type Lisp que pour les langages de type ;C. C'est une caractéristique fondamentale de la langue que l'on ne peut pas facilement remplacer. Cependant, contrairement aux langages de style C, Lisp a tendance à construire des abstractions syntaxiques en masquant complètement la syntaxe du niveau inférieur. Et c'est peut-être la raison pour laquelle vous ne le voyez pas prognsouvent, mais c'est l'un des éléments constitutifs importants, lorsqu'il s'agit de construire des abstractions de langage de niveau supérieur (macros sa).

wvxvw
la source