Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Why do Lisps separate these related semantics?

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


That's closer to cond:

  (cond
    (<condition1> <body>*)
    (<condition2> <body>*)
    (else <body>))


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


Ah. I forgot about that.

See, this is what happens when you know Scheme better than CL.


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

Info here:

http://franz.com/~jkf/coding_standards.html

http://franz.com/support/documentation/6.0/doc/operators/exc...

Your idea is certainly doable:

  (my-if cond
    (then-form then-form ...)
    (else-form else-form ...))
The macro is trivial:

  (defmacro my-if (cond then-forms &optional else-forms)
    `(if ,cond
       (progn ,@then-forms)
       (progn ,@else-forms)))
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:

  (cond
    ((some condition)
       (consequent)
       (consequent2)
       (consequent3))
    ((other condition)
       (other)
       (other2))
    (t (alternative)
       (alternative)))
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))




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: