19

I have seen progn being used quite a lot as I browse the configuration files of experienced Emacs users. I found this nice explanation of progn, but what I am really curious about is, what is the benefit of using this function? Take for example this snippet (taken from Sacha Chua's configuration):

(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)))

Is there any major difference between the above configuration and this?

(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))

I feel like the first example is somehow cleaner, even though it has more syntax, and my intuition is that there might be some kind of performance boost from using progn, but I am not sure. Thank you for any insights!

  • 6
    In this particular case there is no difference: use-package will wrap a progn around your :config forms if it is missing. Try it out: you can put point at the end of a (use-package ...) and call M-x pp-macroexpand-last-sexp to see how the macro is expanded. You'll see that it is identical for these two examples. – glucas Dec 3 '15 at 4:23
  • An example where progn is needed: emacs.stackexchange.com/questions/39172/… – npostavs Mar 3 '18 at 23:58
11

progn is typically used when dealing with macros. Some macros (use-package is a macro, last I checked) accept only 1 form, where others consume all remaining forms.

progn is used in the former case to turn a sequence of forms into a single form.

In your examples, the first one uses progn and thus there is 1 form after :config. In the second, there are 3 forms. If the use-package macro only expects 1 form following :config, then it will cause an error.

It is worth noting that using progn works in both cases, while omitting it only works if the macro accepts multiple forms. As a result of this, some people prefer to simply always use progn, because it will always work.

  • 15
    It really has nothing to do with macros. Some functions and macros have a so-called "implicit progn", meaning that they accept any number of sexps as separate arguments and evaluate them in sequence. Others don't, and instead expect/allow only a single argument where you might want to evaluate multiple sexps in sequence. That's all progn does: it lets you provide a single sexp that when evaluated evaluates the sexp args to progn in sequence. Compare (if true (progn a b c)) with (when true a b c). In both cases, a, b, and c are evaluated in sequence. – Drew Dec 3 '15 at 5:40
  • 2
    use-package allows multiple forms in :config and :init. – lunaryorn Dec 3 '15 at 11:06
  • 4
    I disagree that 99% of the use cases are for macros etc. Any function or macro can be passed a progn sexp that contains multiple sexps that are evaluated for their side effects, before returning the result of the last of these. It really has nothing to do with macros. Macros "as an example" is fine. Giving the impression that progn exists for macros, and that 99% of its use is for macros, is misleading, IMO. progn is about shoveling a sequence of evaluations into a single sexp (to fit a given expected argument), and it is therefore about side effects. – Drew Dec 3 '15 at 14:47
  • 5
    1. progn was introduced in Lisp 1.5 (see section The Program Feature), in 1962, long before macros were added to Lisp. The purpose is to sequentially evaluate expressions for their side effects. 2. See progn for how Emacs itself introduces progn to Elisp programmers. See the zap-to-char code for a simple use case. – Drew Dec 3 '15 at 17:42
  • 2
    We can agree to disagree. My point is that progn has nothing whatsoever to do with macros. Its purpose is of course "to pass multiple forms as a single form" (your words) - whether to a function or a macro or a special form, but also to "Eval BODY forms sequentially and return value of last one" (doc string). Now as ever ("these days", indeed!). An ordered evaluation is important only for side effects, obviously. A given use case (macro or not) might or might not care about the evaluation order, but that is irrelevant. And Emacs Lisp is not exceptional in any way in this regard. – Drew Dec 4 '15 at 2:54
10

The most important reason for progn is described in the first line of the progn documentation (emphasis added):

progn is a special form that causes each of its arguments to be evaluated in sequence and then returns the value of the last one.

Addendum:

Without progn, sequence is not guaranteed, especially if subsequent expressions are dependent on the side effects or return values of the previous expressions. progn enforces execution sequence the same as textual sequence. Helps to not confuse execution with parsing. This behavior goes back to the fundamentals of lisp control structures and functional programming. Here's an excerpt (emphasis added) from the lisp reference manual:

The built-in control structures are special forms since their subforms are not necessarily evaluated or not evaluated sequentially.

Does progn boost performance?

Parsing performance, no. Execution performance, no. At best it may equal but never magically boost performance.

When is progn used?

...most often inside an unwind-protect, and, or, or in the then-part of an if.

  • I'm not sure why that's so important. Emacs always evaluates sequentially. – lunaryorn Dec 3 '15 at 11:05
  • 2
    @lunaryorn see the updated answer. – Emacs User Dec 3 '15 at 15:30
  • I'm not sure whether I understand your edit. Naturally there are special forms with a different evaluation order (e.g. if), but the normal evaluation order (e.g. top-level forms, function bodies, etc.) is textual. Sure, to some degree that's an implicit progn, but that's usually not what you use progn for in your own code. – lunaryorn Dec 3 '15 at 15:39
  • I think the Elisp manual node (elisp) Sequencing which you link says it all. – Basil May 17 '18 at 2:46
10

A better way to understand what progn is is by comparing it to the family: prog1 and prog2. The n or 1 or 2 part of the name stands for the statement from the list whose result you are interested in. In other words, progn will return the result of the last statement it contains, whereas prog1 will return the first, and similar for prog2.

Today this functionality seems a little awkward since we learn to either expect the program to return in the last statement, or to instruct it explicitly what to return. Thus prog1 and prog2 are very seldom used. However, if you think about it, it makes sense, and here is how:

Different programming languages use different strategies to describe their semantics. Lisp family used to be tightly tied to denotational semantics. Without going into much detail, this kind of semantics has particular difficulty with something we grew to know as "statements", which are not "expressions". Meanings of code are typically thought in terms of function combinations, while a "statement" which cannot be even described as a function (since it doesn't evaluate to anything) is thus difficult to deal with. However, since Lisp permits side-effects, sometimes a programmer would want to use an expression without using its value in a way that it doesn't affect the expression following it. And this is where progX family comes in.

In C-like languages this feature is sometimes known as sequence point (i.e. ; and , for example). progn is as essential to Lisp-like languages as ; is to C-like languages. It is a fundamental feature of the language one cannot easily replace. However, unlike in C-style languages, Lisp tends to build syntactical abstractions by completely hiding the syntax of the lower level. And this perhaps why you don't see progn used all that often, yet it is one of the important building blocks, when it comes to building higher level language abstractions (s.a. macros).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.