Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ELisp: How to execute several expressions in 'else' block?

Tags:

emacs

elisp

How can I execute more than one expression in the else block of an if statement, when using ELisp?

like image 502
Dean Chen Avatar asked Dec 04 '22 04:12

Dean Chen


2 Answers

You don't need progn for this, as this is already the default behaviour:

C-hf if RET

if is a special form in `C source code'.

(if COND THEN ELSE...)

If COND yields non-nil, do THEN, else do ELSE...
Returns the value of THEN or the value of the last of the ELSE's.
THEN must be one expression, but ELSE... can be zero or more expressions.
If COND yields nil, and there are no ELSE's, the value is nil.

To re-use sindikat's example:

(if (> 1 2)
    (message "True")
  (message "False")
  (message "I repeat, completely false"))

You would, of course, use progn if you wished to evaluate multiple expressions in the THEN form.

like image 176
phils Avatar answered Dec 28 '22 00:12

phils


phils's answer is correct and idiomatic. If you want to execute several statements in the else block of if, and you're concerned only with Emacs Lisp, go with his answer.

In Emacs Lisp and Common Lisp when you want to perform multiple actions in sequence for side effects (like change a variable, print something on screen, generally, anything that has nothing to do with function returning a value), and then return the value of only one of the expressions, you use progn, prog1, or prog2. They all evaluate all the forms inside them, but return only the value of last, first and second expression respectively.

(progn 1 2 3 4 5) returns 5, (prog1 1 2 3 4 5) returns 1, (prog2 1 2 3 4 5) returns 2. This is clearly useless, because the point of evaluating forms inside progn, prog1, prog2 is for side effects. So all of the following would print string α, β, γ, δ in echo area (and in *Messages* buffer), but only one of the strings will be returned:

(progn (message "α") (message "β") (message "γ") (message "δ")) ; => "δ"
(prog1 (message "α") (message "β") (message "γ") (message "δ")) ; => "α"
(prog2 (message "α") (message "β") (message "γ") (message "δ")) ; => "β"

All 3 functions exist both in Emacs Lisp and Common Lisp. But what if you want to return the 3rd expression's value? There is no prog3. Combine existing ones! All of the following return γ:

(progn (message "α") (message "β") (prog1 (message "γ") (message "δ"))) ; => "γ"
(prog1 (progn (message "α") (message "β") (message "γ")) (message "δ")) ; => "γ"
(prog2 (message "α") (progn (message "β") (message "γ")) (message "δ")) ; => "γ"
(prog2 (message "α") (prog2 (message "β") (message "γ") (message "δ"))) ; => "γ"

In Common Lisp to group statements together and return only the last one's value in if, you would do:

(if t
    (progn (message "ε") (message "ζ"))
    (progn (message "η") (message "θ"))) ; returns ζ
(if nil
    (progn (message "ε") (message "ζ"))
    (progn (message "η") (message "θ"))) ; returns θ

In Emacs Lisp you still have to group statement in then block with progn, prog1, prog2, but you in else block you don't have to, thus phils's answer. In Emacs Lisp (if nil 1 2 3) would return 3, while in Common Lisp it would err with too many parameters for special operator IF.

See also: Section "8.1.4 The 'progn' Special Form" from "Emacs Lisp Intro".

like image 21
Mirzhan Irkegulov Avatar answered Dec 28 '22 01:12

Mirzhan Irkegulov