17

This is a follow-up on the comments on this answer. The following bits of code seem to be equivalent:

(and a b)

(when a b)

Of course and lets you put more conditions: (and a b c d) means (when (and a b c) d)

I tend to use when only to express branching. Are there actual differences? Is it better to use one or the other?

I don't have Emacs' C source at hand; and is a C function; when is a macro that expands to if, which itself is a C function.

Clément
  • 3,924
  • 1
  • 22
  • 37
  • "Of course and lets" should be "Of course \`and\` lets", but that's only a 2 character change and isn't an allowed edit. There's no override. – Harvey Aug 13 '15 at 14:25

2 Answers2

21

TL;DR: when is about side effects, and is for pure boolean expressions.

As you've noticed, and and when differ only in syntax, but are otherwise entirely equivalent.

The syntactic difference is quite important, though: when wraps an implicit progn around all but the first argument forms. progn is an inherently imperative feature: It evaluates all but the very last body form for their side effects only, discarding whatever value they returned.

As such, when is an imperative form as well: It's main purpose is to wrap side-effecting forms, because only the value of the very last form actually matters for the body.

and on the other hand is a pure function, whose main purpose is to look at the return values of the given argument forms: Unless you explicitly wrap progn around any of its arguments, the value of every argument form is important, and no value is ever ignored.

Hence, the real difference between and and when is stylistic: You use and for pure boolean expressions, and when to put a guard around side-effecting forms.

Hence, these are bad style:

;; `when' used for a pure boolean expression
(let ((use-buffer (when (buffer-live-p buffer)
                    (file-exists-p (buffer-file-name buffer)))))
  ...)

;; `and' used as guard around a side-effecting form
(and (buffer-file-name buffer) (write-region nil nil (buffer-file-name buffer)))

And these are good:

(let ((use-buffer (and (buffer-live-p buffer)
                       (file-exists-p (buffer-file-name buffer)))))
  ...)

(when (buffer-file-name buffer)
 (write-region nil nil (buffer-file-name buffer)))

I know that some people disagree about this, and happily use and to guard side-effects, but I think that this is really bad style. We have these different forms for a reason: Syntax matters. If it didn't, we'd all only ever use if, which is the only conditional form you really need in Emacs Lisp semantically. All other boolean and conditional forms can be written in terms of if.

tarsius
  • 25,298
  • 4
  • 69
  • 109
  • +1; I tend to agree with the stylistic argument. I'll wait to see if someone comes up with another explanation before accepting. – Clément Jul 23 '15 at 21:20
  • 1
    Forgive this sarcastic remark, but `and` beeing a pure function reminds me of people having reinterpreted the bible at various times to coincide with the norms of the day. – politza Jul 23 '15 at 21:23
  • 2
    IMO (i.e., in the style I use), `and` is about *caring about the return value*. It is not necessarily not about using side effects. If my program cares about the return value then I use `and`. If it does not then I use `when`. IOW, I use `when` *only* for side effects, but I might well use a `progn` in one of the args to `and`. It's about whether the return value matters, not about whether it *alone* matters. – Drew Jul 23 '15 at 21:39
  • Your boolean vs. imperative difference is the key. Thanks for mentioning that. – Emacs User Jul 23 '15 at 23:19
  • 5
    `and` is not a function in any Lisp I know of. In Emacs Lisp it's a special form, in Common Lisp it's a macro, simply because it's expected to have short-circuit behavior (impossible to achieve with functions, because they always evaluate their arguments). – Mark Karpov Jul 24 '15 at 09:10
  • @Mark While technically correct, does this have any relevance to the stylistic difference between `and` and `when`? If you think so—I don't—please feel free to edit my answer accordingly. –  Jul 24 '15 at 09:14
  • 2
    @lunaryorn, Yes, it's not really relevant, but it's true, so I think it's incorrect to write that `and` is a function, when it's not. It doesn't matter how relevant the fact is to the question, we should always use correct terms, that's all. – Mark Karpov Jul 24 '15 at 09:20
  • @Mark is right, and then slightly wrong where he backs off too much from his initial right. It is incorrect to say that `and` never ignores an argument. In fact, if the first argument to `and` evaluates to `nil`, the remaining arguments are not evaluated. This is important in usages where evaluating those arguments could lead to errors or lengthy computations. Example idiom: `(and (boundp 'foo) foo)`. – Harald Hanche-Olsen Jul 24 '15 at 09:56
  • @HaraldHanche-Olsen I wrote about the _value_ of an argument, that is, _after_ evaluation, not the argument itself. –  Jul 24 '15 at 10:57
  • 2
    Well, I don't think “the value of every argument form is important” is consistent with what that interpretation. It could have been better expressed by saying that no form is evaluated only to have its value thrown away. – Harald Hanche-Olsen Jul 24 '15 at 11:18
  • 3
    Somewhat OT, but: The difference is more than stylistic in Lisps that don't pun `nil` to mean all of "false", "the empty list", "void", etc. For example in Scheme and Racket the non-truthy result of `when` is `void` (i.e. "no meaning") whereas for `and` it is `#f` ("false"). Although this is N/A for Elisp, it's something a Lisp generalist might consider. – Greg Hendershott Aug 14 '15 at 18:58
6

Let me start off by saying that (and a b) and (when a b) in your example do the same thing: First a is evaluated. b is evaluated if a is true#.

But and and when are used for different things.

  • You would use (and a b) to return true# if BOTH a and b are true# (or non-nil); and nil otherwise.

  • You would use (when a b) or to be more correct,

    (when a
       ;; do something like b
       )
    

    when you want the "b" code to execute if and only if a is true#.


Here is an example where you use when and and together:

(when (and a b)
   (do-c))

Above, the function do-c is called ONLY if BOTH a and b are true#.


References for study

# All references to true refer to the Boolean TRUE.

Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
  • 4
    `and` doesn't return `t` if all its arguments are non-nil, but the value of the last argument. – Meaningful Username Jul 23 '15 at 17:37
  • 1
    By `t`, I meant the Boolean TRUE or non-nil. Thanks, I will clarify that in my answer. – Kaushal Modi Jul 23 '15 at 17:41
  • I don't think your answer goes much further that what I already stated in the question; I know what both do, and the final example you give already appears in my question (`(when (and a b) c)`). In addition, the part about how `and` and `when` suggests that that's actually how they are used in general, but the very basis for this question was the fact that I often see them used differently (see for example the answer I linked to in my question). – Clément Jul 23 '15 at 21:18
  • From a purely functional construct point-of-view, **when** is for non-terminating evaluations and **and** is for direct symbols and boolean logic. Functional programming needs this distinction; imperative programming fudge this away. – Emacs User Jul 23 '15 at 23:25
  • @Clément I tried to emphasize that even though `and` and `when` give the same result, we generally use `and` when you need a form to return nil or non-nil boolean status and `when` for conditionals. Also note the difference in the style of how they are generally written, where to new line, where to indent,.. in order to convey a clear distinctions in what those constructs convey. In summary: stylistic. – Kaushal Modi Jul 23 '15 at 23:32
  • Emacs User: Can you clarify? I don't really see what functionality has to do here. Do you mean purity? – Clément Jul 24 '15 at 00:16
  • 1
    I don't think "boolean true" is more clear than just "true". – YoungFrog Jul 24 '15 at 05:57