(if <predicate> <consequent> <alternative>)
(cond (<p1> <e1>)
(<p2> <e2>)
..........
(<pn> <en>))
A minor difference between
if
andcond
is that thecond
's expression part of eachcond
clause may be a sequence of expressions. -- SICP
I wonder why the designers of the scheme language made the expression for if and cond different.
What's the purpose of that design?
CodePudding user response:
In a language like Scheme which is not purely functional, it is often useful to allow a sequence of expressions where there is room for it in the syntax: for instance in the body of a procedure and so on. So for instance a purely functional Lisp might have a syntax for functions which was
(λ (<arg> ...)
<expression>)
But Scheme allows
(λ (<arg> ...)
<expression-1>
<expression-2>
...)
Where the values of all but the last expression are ignored: they just happen for side-effect. And since there is room for this in the syntax, Scheme allows it.
However there is simply no room in the syntax of if
for this to be the case (see below).
It would be possible to design multiway conditional expression where there was also no room in the syntax for it, which might look like:
(kond
a 1
b 2
c 3
else 4)
for instance (here else
is magic to kond
: I can't remember if SICP's Scheme has that).
But if you consider what cond
's syntax actually is:
(cond
(a 1)
(b 2)
(c 3)
(else 4))
Then there is obviously now room in the syntax to write a sequence of expressions in the result position of each clause. And Scheme does therefore allow that because there simply is no reason not to do so. So instead of
(cond
(<t> <e>)
...)
You can write
(cond
(<t> <e1> <e2> ...)
...)
For instance:
(cond
(world-has-ended
(displayln "The world has ended: rain of fire imminent")
(rain-fire-from-sky 'yes-really))
...)
In fact, Scheme has an operator, begin
whose whole purpose is to allow a sequence of expressions where only one is allowed. So for instance if you want to have a sequence of expressions where there would naturally only be one, you can use begin
:
(if world-has-ended
(begin
(displayln "The world has ended: rain of fire imminent")
(rain-fire-from-sky 'yes-really))
(begin
(displayln "World has not yet ended, sorry for the frogs")
(rain-frogs-from-sky)))
You can then think of cond
as defined in terms of if
and begin
:
(cond
(world-has-ended
(displayln "The world has ended: rain of fire imminent")
(rain-fire-from-sky 'also-rocks))
(world-has-nearly-ended
(displayln "The world has nearly ended")
(rain-frogs-from-sky 'also-some-fire)))
is the same as
(if world-has-ended
(begin
(displayln "The world has ended: rain of fire imminent")
(rain-fire-from-sky 'also-rocks))
(if world-has-nearly-ended
(begin
(displayln "The world has nearly ended")
(rain-frogs-from-sky 'also-some-fire))
#f))
As to why the syntax of cond
was designed so there is room for multiple expressions: that's a question for history. Two things I think help to explain it:
- A syntax like
(cond <t1> <e1> <t2> <e2> ...)
is a pain to read since you need to count the number of forms in the body, while(cond (<t1> ...) (<t2> ...) ...)
is much easier. - In the early days of Lisp people who had exposure only to FORTRAN and assembler (because that's pretty much all there was) tended to write programs which were, well, like programs in FORTRAN, and had a lot of imperative operations, side-effects, sequencing and so on. And the syntax of
cond
allows for that.
CodePudding user response:
If you allow any number of expressions in if
, how do you tell when the true ones end and the false ones begin?¹ With cond
, each case is a list where the first element is the test expression and the rest of the list gets evaluated when true. There's no ambiguity.
1: With begin
:
(if <test>
(begin <true exprs> ...)
(begin <false exprs> ...))
cond
will typically be a macro that expands to
(if <p1>
(begin <e1>)
(if <p2>
(begin <e2>)
...))