cond actually is a predecessor of if/else you know from other languages. Yes, it was first. As for progn, it's pretty much the equivalent of curly braces from C-like languages (or of the comma operator, if you care about its value).
Why if seems to be commonly defined as (condition then-clause &rest else-clauses) instead of (condition then-clause else-clause), I don't know. History/common use patterns, probably?
It's not really separating related semantics; it's that an if-then-else option has to have both a then clause and and else clause. (IF thenelse) is a natural way to express that, but it does mean that if you want to do more than one thing in the then clause then you'll need to have a PROGN. You could have multiple statements in the else clause (which is what emacs does).
In Common Lisp, the syntax is:
if test-form then-form [else-form] => result*
In emacs, it's:
if test-form then-form [else-form]* => result*
I think that the emacs form is weird and annoying, but might make certain forms of code easier (e.g. check for something and short-circuit, else calculate something more deeply).
COND is a different beast entirely.
Note that WHEN & UNLESS both have an implicit PROGN.
OK, so the weirdness is really that Emacs' else-form accepts multiple statements, and/or that there is no implicit progn for both then-form and else-form.
I.e, I would've expected something like this:
(if (condition)
(
(then do something)
(and more things)
)
(
(else do something else)
(and more other things)
)
)
(and yeah, I'm definitely showing my C-semantic preferences here aren't I?)
Yup. And the reason not to default to a list of then-forms and a list of else-forms is partly æsthetic, but partly dealing with the common case, because:
(if (eq foo bar) (baz))
looks like a function call, not returning the value of baz (using your syntax).
And of course Lisp is often written in a semi-functional way, so PROGN is, while not rare or really even avoided, not the usual way of doing things, usually.
Not in Common Lisp, that: else is just a variable reference here. Unbound, if you're lucky; bound to a true value if you're somewhat less lucky, and bound to nil if you're haplessly unfortunate. :)
There cannot be an implicit progn for both! Because we don't know where one ends and the other begins.
That is, unless we introduce a signal, like an else keyword/label which separates them, like this:
(if cond form form form
else form form form)
Such macros have existed. A certain John Foderaro of Franz, Inc. was (is?) known for favoring and promoting his if{star} macro. (Of course I mean asterisk by {star} which HN won't let me type).
I've always resisted writing this macro because I felt it has a slight readability issue:
(my-if (some condition)
((consequent)
(consequent2)
(consequent3))
((alternative) ;; relatively quiet signal here
(alternative)))
Maybe it's not that bad, after all. Still, it's just saving a few keystrokes to insert progn and doesn't buy much over cond, which is more general, allowing multiple conditions:
NOTE: cond existed in Lisp first, as a special form. The if macro came later as a syntactic sugar for simple situations involving just one condition!
The thing is that if you keep most of your code functional, a lot of this is moot. We need multiple statements when we are doing something imperative. There is never any reason to have (progn S1 S2) unless S1 has a side effect. If S1 has no side effect, then this is equivalent to (progn S2), which is just S2. If you avoid side effects, the you don't need any progn most of the time (implicit or not).
This is why progn is called what it is; it's Lisp's feature for writing a "program" (in the sense of an imperative list of things to do). Well, "prog" is that feature; and "progn" is the variant which returns the value of the n-th form.
Another mitigation is that lot of the time code binds variables anyway with a let:
(if condition
(let (... vars ...)
this
that)
(let (.... other vars ...)
other))
That is true, but somewhat alleviated by Emacs' choice to indent the THEN form at a different level than [ELSE]*. A novice would notice that the code looks incorrect as they were typing it.
Firstly, because the way Lisp is structured makes it impractical to have implicit progn (or implicit begin in the schemes) in if.
Secondly, because lisp, while not a functional language, has functional leanings: you would be surprised how often it is that one form is all you need.
CL-USER 48 > (LOOP REPEAT 2
FOR number = (READ)
IF (> number 0) DO
(princ '(the number is larger than 0))
(print 'true)
(terpri)
ELSE DO
(princ '(the number is not larger than 0))
(print 'false)
(terpri))
2
(THE NUMBER IS LARGER THAN 0)
TRUE
-2
(THE NUMBER IS NOT LARGER THAN 0)
FALSE
NIL
It is also not that difficult to write a multi-expression IF. Here is a sketch:
Edit: thanks folks! You've helped me get over the facepalm from years ago :)