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