6

I expected the outcome of the following small program:

(progn
  (setq x 1)
  (case x
    (x 'Yes)
    (t 'No)))

to be Yes, given that case compares things with eql, and surely x is eql to itself. However the outcome is No and I am completely puzzzled by this!



This is a long comment, not an answer!

Thanks to everybody for making everything perfectly clear. My original intention was to use 'case' to simulate something like

(cond
  ((equal x a) 'this)
  ((equal x b) 'that)
  ((equal x c) 'something-else))

which would look much cleaner if it were equivalent to

(case x
  (a 'this)
  (b 'that)
  (c 'something-else))

But, alas, I understand that these are not equivalent forms due to the fact that case evaluates x but does not evaluates the keys in each clause.

I thought of faking things, such as in

(let ( (x 2) (a 1) (b 2) (c 3) ) (flet ( (eql (x y) (equal x (eval y))) )
  (case x
    (a 'this)
    (b 'that)
    (c 'something-else))
))

which does work as I'd like but I suppose it is a bit blasphemous to alter the meaning of something as basic as 'eql', even if only temporarily!

5
  • 1
    FYI it is recommended to use cl-case from cl-lib.el instead of case from cl.el. The behaviour you see is not a bug; it is a feature of elisp macros. As the docstring of cl-case describes, the first occurence of x is evaluated as an expression, but the second occurence of x stands for the symbol 'x, i.e. the comparison being made is (eql x 'x), which returns nil.
    – Basil
    Oct 26 '17 at 19:42
  • If you are interested in reading more about this, see (cl) Conditionals and (elisp) Macros. See (elisp) nil and t for why those two symbols are seemingly handled differently in cl-case.
    – Basil
    Oct 26 '17 at 19:45
  • @Basil, thanks for your answer. However, I did not find the explanation you gave in the docstring. Anyways, this sounds really strange to me!
    – Ruy
    Oct 26 '17 at 20:13
  • @Basil, do you know an elisp function which looks like cl-case, but which makes the comparison using equal and which evaluates things in the same way, i.e, without this weird interpretation of symbols? I know one could use cond, but I feel a case function is sometimes better.
    – Ruy
    Oct 26 '17 at 20:26
  • See the excellent answers so far. In particular refer to Stefan's answer for an alternative to cl-case and cond.
    – Basil
    Oct 26 '17 at 23:35
11
  1. case is an alias for cl-case, so there is no difference, there.

  2. This aspect of the behavior is the same in Common Lisp.

  3. There is nothing very weird here. You just need to read the doc, which tells you that the first arg to case is evaluated and the result is compared with the car entries of the following lists. cl-case is a macro, so in general (i.e., unless told otherwise) you can suppose that it does not evaluate its arguments.

  4. But perhaps the doc should explicitly point out that nothing in those lists is evaluated - only the first argument is evaluated.

  5. More generally wrt the doc: This is an emulation of case in Common Lisp. The doc for case in Common Lisp itself is very clear: either the hyperspec or (especially) Common Lisp the Language.

  6. Unfortunately nearly ALL of the Emacs emulation of Common-Lisp stuff is poorly documented. One really has to check the Common-Lisp doc to understand what it is that Emacs is trying to emulate - and then guess the ways in which it doesn't succeed in emulating this or that bit.

  7. Perhaps this is really what you were trying to do (just a guess):

    (case x
      (1 'Yes)
      (t 'No))
    
7
  • I don't know which version of the docstring you were looking at, but I don't find the one I have in front of me (Emacs 24.4) clear about the fact that the key is not evaluated. “EXPR is evaluated and compared against each key in each KEYLIST (…). A single atom may be used in place of a KEYLIST of one atom.” Oct 26 '17 at 21:53
  • To clarify: In my comment I did not mean nil and t are handled differently by case, but rather that they are special symbols which evaluate to themselves.
    – Basil
    Oct 26 '17 at 23:30
  • 1
    In addition, I did not suggest case and cl-case behave differently (though this is not impossible across Emacs versions); rather that the cl- namespace is preferred over cl.el in new elisp.
    – Basil
    Oct 26 '17 at 23:38
  • Great answer, thanks! :) Alas comments can't be edited for clarity.
    – Basil
    Oct 27 '17 at 0:25
  • @Basil: Comments can be deleted. New comments can replace them, if need be. Delete + insert = replace = edit, sort of.
    – Drew
    Oct 27 '17 at 0:28
7

BTW, I recommend the use of pcase, which is more powerful. The equivalent code to what you wrote, would be:

(let ((x 1))
  (pcase x
    ('x 'Yes)
    (_ 'No)))

Making it clear that x is not evaluated. This said, it would probably confuse you as well, because if you naively write:

(let ((x 1))
  (pcase x
    (x 'Yes)
    (_ 'No)))

it will tell you Yes but for the wrong reason: the x pattern matches anything (not just the value held in variable x) and it then re-binds a new variable x, so

(let ((x 1))
  (pcase 5
    (x x)
    (_ 'No)))

would return 5. But with pcase you can do the test you want, which is written in a significantly more verbose way:

(let ((x 1))
  (pcase x
    ((pred (eql x)) 'Yes)
    (_ 'No)))
4

The first part of each clause in a case form is a value (or a list of values), not an expression. The x before 'Yes is syntactically interpreted as a value. The x after case, on the other hand, is interpreted as an expression, i.e. it is evaluated; its value is 1. The value 1 (which is an integer) is not equal to the value x (which is a symbol). Contrast:

  • (case x (x 'Yes) (t 'No))No   (1x)
  • (case 'x (x 'Yes) (t 'No))Yes   (x = x)
  • (case x (1 'Yes) (t 'No))Yes   (1 = 1)

case is meant to be similar to case statements in most other languages, which compares the value of an expression against some constants. Each clause of a case statement specifies a constant (i.e. something that is already in the form of a value in the source code) to compare against the value of the expression.

(case (+ x y)
  (1 "one")
  (2 "two")
  ((3 4) "many")    ; instead of a single value, a clause can have a list of values
  (t "lots"))

If you want to test whether the value of an expression is equal to the value of one of several expressions, then you're doing something more complicated than what case can offer. Using cond is perfectly reasonable.

https://www.gnu.org/software/emacs/manual/html_node/cl/Conditionals.html#index-cl_002dcase-32

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.