I have read that by default, a function returns the value of the last expression evaluated as the return value.

Does it mean that when using special form let, the value of the last expression in the let body is what is returned?

If I use list in the following way, only (region-end) will be inserted in the list?

  (let ( (dflt t) ) 

3 Answers 3


"Function" let is a special form.

Like Lisp macros, special forms do not follow any particular rule about evaluating arguments or what to return. Each special form has its own such behavior.

Consult the doc of a given special form for the answer (or the implementation, if the doc doesn't tell you). It suffices to use C-h f let:

let is a special form in C source code.


Bind variables according to VARLIST then eval BODY.

The value of the last form in BODY is returned.

Each element of VARLIST is a symbol (which is bound to nil) or a list (SYMBOL VALUEFORM) (which binds SYMBOL to the value of VALUEFORM).

All the VALUEFORMs are evalled before any symbols are bound.

Emacs often refers in a general way to functions, macros, and special forms as "functions". The predicate that tests this sense of the word "function" is fboundp:

  • (fboundp 'let) returns t
  • (fboundp 'forward-char) returns t

What Lisp more specifically calls a "function" is something that can be funcalled or applyed. The predicate that tests this sense of the word "function" is functionp:

  • (functionp 'let) returns nil
  • (functionp 'forward-char) returns t

EDIT add simplified test version

Here is a simplified test, that only contains the essence.


(list (let () 'a 'b))

Then for even better understanding, also evaluate

(let () (list 'a 'b))

Now draw your conclusions

END EDIT The redundant original answer is kept here below

Why not just test it? Evaluate

(defun test ()
   (list (let ((dummy t))

then select a region and run M-x test. How many values does the list contain?

Then place the print list inside the let and do the same

(defun test ()
    (let ((dummy t))
       (list (region-beginning)

Now what (how many values in the list) do you get?

  • Can I test it in the scratch buffer, because currently I have a problem with my init files.
    – Dilna
    Jul 17, 2022 at 8:47
  • Yes, that is more or less what the scratch buffer is for... Jul 17, 2022 at 10:07
  • So you could answer your own question here now. Because, as you have found here now, it does matter where you place the let when you add multiple elements to the list. The implied answer here would be a perfect answer there (you would get my upvote :) Jul 17, 2022 at 10:32

let Bind variables according to VARLIST then eval BODY.

Then the value of let should be the last evaluation inside it. Your list will only contain (region-end) value. list should be the last call inside let if you want it to return a list.

You can look at let as an inline scope.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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