43

When is one better than the other? Is one faster than the other or does the only difference is the return of false or nil?

nbro
  • 15,395
  • 32
  • 113
  • 196
Alonzorz
  • 2,113
  • 4
  • 18
  • 21

3 Answers3

52

Use if when you have exactly one truthy and one falsy case and you don't need an implicit do block. In contrast, when should be used when you only have to handle the truthy case and the implicit do. There is no difference in speed, it's a matter of using the most idiomatic style.

 (if (my-predicate? my-data)
     (do-something my-data)
     (do-something-else my-data))

 (when (my-predicate? my-data)
     (do-something my-data)
     (do-something-additionally my-data))

In the if case, only do-something will be run if my-predicate? returns a truthy result, whereas in the when case, both do-something and do-something-additionally are executed.

schaueho
  • 3,419
  • 1
  • 21
  • 32
  • 4
    What if I only have to handle the true case, and no implicit `do` is needed, which is more conventional to use in this case? – damonh Feb 13 '17 at 13:52
  • 2
    Use `when`. The `do` part is an additional good reason to prefer `when` over `if`, not a necessary precondition. – schaueho Feb 18 '17 at 13:32
10

Use if when you have two different expressions: for true clause and for false clause.

when and when-not are useful in two cases:

  • when you want to perform one or several (implicit do helps here) non-pure operations conditionally;
  • when you want to evaluate something when some predicate evaluates to true (or false in case of when-not), and return nil in opposite case.

only difference is the return of false or nil

There is no major difference between false and nil, as the both evaluate to false in logical context.

Mark Karpov
  • 7,499
  • 2
  • 27
  • 62
  • 1
    I've noticed a stylistic difference in use of `when` in Clojure and Common Lisp. In CL, some people consider it bad to use `when` if you care about the return value when the test fails; in that case you should explicitly return `nil` in the last clause of an `if`. It makes sense that it's common to use `when` in Clojure in the same situation, since most code is purely functional in Clojure: return values are rarely ignored. If Clojure programmers followed the CL convention, `when` would rarely be used in purely functional code. (Occasionally `nil` vs. `false` matters.) – Mars Sep 21 '14 at 05:55
  • @Mars, it is interesting, is there real reason for coexistence of `false` and `nil` in Clojure? I can think of no situation when `false` could do something that `nil` couldn't. – Mark Karpov Sep 21 '14 at 07:28
  • I think the general idea is: `nil` means "there's nothing left in the sequence", while `false` just means false. The fact that `nil` also means false allows testing for the end of a sequence simply. Also, you can do things like `(get {:a 1 :b nil} :c false)`. More discussion [here](http://stackoverflow.com/questions/5830571/why-did-father-of-clojure-say-that-schemes-true-false-are-broken), [here](http://stackoverflow.com/questions/6045404/why-clojure-idiom-prefer-to-return-nil-instead-of-empty-list-like-scheme), [here](http://www.lispcast.com/nil-punning), and [here](http://clojure.org/lazy). – Mars Sep 22 '14 at 05:23
  • Also: Because `nil` means something like "empty sequence" as well as false, `next` and `rest` have meaning when applied to `nil`, but throw exceptions when applied to `false`. Other interesting uses of `nil` include [`some`](http://clojuredocs.org/clojure_core/clojure.core/some), which can function like a predicate because it returns nil on failure, but isn't really a predicate (therefore it has no "?" in it's name), and [`keep`](http://clojuredocs.org/clojure_core/clojure.core/keep) (see doc page). However, I don't fully understand why Clojure should have both `nil` and `false`. – Mars Sep 22 '14 at 05:40
  • 1
    @Mars In CL, the recommendation is basically the same as for Clojure, cf. https://google-styleguide.googlecode.com/svn/trunk/lispguide.xml#Conditional_Expressions and https://www.cs.cmu.edu/Groups/AI/html/faqs/lang/lisp/part1/faq-doc-4.html. From the latter: _Never use a 2-argument IF or a 3-argument `IF` with a second argument of `NIL` unless you want to emphasize the return value; use `WHEN` and `UNLESS` instead. You will want to emphasize the return value when the IF clause is embedded within a `SETQ`, such as `(SETQ X (IF (EQ Y Z) 2 NIL))`._ – schaueho Sep 22 '14 at 10:20
0

Since Clojure is still relatively young I think it's worth looking into the much longer history of Lisp practice for guidance here.

Originally when was to be used if you have a side effect (e.g. see 1, 2 among others). Why? Well it doesn't have an explicit return value in the falsy case implying you don't really care about it, and it also has an implicit do block (or progn in Lisp parlor) which only makes sense if you have side effects.

Sometime in the 90s or so the focus shifted though and most guides start to suggest to use when instead of an if with only one branch without regards to side effects (for a summary see 3). Why? Well, if you have an if with only one branch it can leave open the question if you missed to provide the alternative branch or if you even have a misplaced parenthesis. In the best case this can lead to confusion, in the worst case to hidden bugs.

The difference is subtle though, if you rely on the implicit nil for if in the falsy case maybe you should make it explicit. Otherwise you probably already rely on a side effect of the truthy branch so when would be appropriate under both guidances. If your expression is functional and should be preferred anyway (e.g. see 2), the difference here is that you might get false instead of nil in the falsy case though. But again: if you rely on getting nil why not make it explicit? (In other words: I think linting rules like the example in 4 are misguided.)

Sometimes the rationale for using when over if is given as: one should use the most specific construct one needs in the particular case (e.g. see 5). But that same argument can be turned around: don't use when if you don't need the implicit do form. So while a good guiding principle in general it can not be really applied here.

So I tend to use when only when there is a side effect as otherwise there are usually constructs with less imperative smell available (like and). That is not to say that an if form should only be used for expressions without side effects, as it might very well be that you need a side effect for both branches.

David Ongaro
  • 3,568
  • 1
  • 24
  • 36