Q: how can I let-bind multiple variables conditional on x?

Assume that I have three variables, a, b, and c, that I want to let-bind conditional on some other x. How do I do that concisely and idiomatically?

Here's an example that works, but is repetitive and error-prone:

(let ((a (if t 1 4))
      (b (if t 2 5))
      (c (if t 3 6)))
  (list a b c))                         ; => (1 2 3)

Here's another example that works, but the use of setq feels both repetitive and inelegant:

(let (a b c)
  (if t
      (setq a 1
            b 2
            c 3)
    (setq a 4
          b 5
          c 6))
  (list a b c))                         ; => (1 2 3)

Here's an example that doesn't work, because the variables fall outside the scope of the let-binding:

(if t
    (let ((a 1)
          (b 2)
          (c 3)))
  (let ((a 4)
        (b 5)
        (c 6)))
  (list a b c))                         ; => nil

What is the idiomatic way to do this?


I think a reasonable way is to collect all the values into a list and then use a destructuring bind, e.g., pcase-let:

(pcase-let ((`(,a ,b ,c)
             (if t (list 1 2 3) (list 4 5 6))))
  (list a b c))

or cl-multiple-value-bind (a bit cleaner than the pcase version since it is specialised to lists, and only allows a single binding clause):

(cl-multiple-value-bind (a b c)
    (if t (list 1 2 3) (list 4 5 6))
  (list a b c))

There is no idiomatic way to do it, IMO. Do what you like.

If you want to be sure to test the condition only once that you are currently retesting, then here is another possibility, where I've used function my-test instead of your vague "x" or "X" (which doesn't seem to actually be referenced in the code you show, which uses t instead):

(let* ((test  (my-test))
       (a     (if test 1 4))
       (b     (if test 2 5))
       (c     (if test 3 6)))
  (list a b c))

Or if you care about the bindings of a, b, and c not being sequential:

(let ((test  (my-test)))
  (let ((a  (if test 1 4))
        (b  (if test 2 5))
        (c  (if test 3 6)))
     (list a b c))
  • Ah, the double-let does make it cleaner. I'll leave it open a little longer to see if there are any other suggestions.
    – Dan
    Sep 21 '17 at 14:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.