Function: letrec

letrec is a macro defined in subr.el.gz.

Signature

(letrec BINDERS &rest BODY)

Documentation

Bind variables according to BINDERS then eval BODY.

The value of the last form in BODY is returned. Each element of BINDERS is a list (SYMBOL VALUEFORM) that binds SYMBOL to the value of VALUEFORM.

The main difference between this macro and let/let* is that all symbols are bound before any of the VALUEFORMs are evalled.

View in manual

Probably introduced at or before Emacs version 24.1.

Source Code

;; Defined in /usr/src/emacs/lisp/subr.el.gz
(defmacro letrec (binders &rest body)
  "Bind variables according to BINDERS then eval BODY.
The value of the last form in BODY is returned.
Each element of BINDERS is a list (SYMBOL VALUEFORM) that binds
SYMBOL to the value of VALUEFORM.

The main difference between this macro and `let'/`let*' is that
all symbols are bound before any of the VALUEFORMs are evalled."
  ;; Useful only in lexical-binding mode.
  ;; As a special-form, we could implement it more efficiently (and cleanly,
  ;; making the vars actually unbound during evaluation of the binders).
  (declare (debug let) (indent 1))
  ;; Use plain `let*' for the non-recursive definitions.
  ;; This only handles the case where the first few definitions are not
  ;; recursive.  Nothing as fancy as an SCC analysis.
  (let ((seqbinds nil))
    ;; Our args haven't yet been macro-expanded, so `macroexp--fgrep'
    ;; may fail to see references that will be introduced later by
    ;; macroexpansion.  We could call `macroexpand-all' to avoid that,
    ;; but in order to avoid that, we instead check to see if the binders
    ;; appear in the macroexp environment, since that's how references can be
    ;; introduced later on.
    (unless (macroexp--fgrep binders macroexpand-all-environment)
      (while (and binders
                  (null (macroexp--fgrep binders (nth 1 (car binders)))))
        (push (pop binders) seqbinds)))
    (let ((nbody (if (null binders)
                     (macroexp-progn body)
                   `(let ,(mapcar #'car binders)
                      ,@(mapcar (lambda (binder) `(setq ,@binder)) binders)
                      ,@body))))
      (cond
       ;; All bindings are recursive.
       ((null seqbinds) nbody)
       ;; Special case for trivial uses.
       ((and (symbolp nbody) (null (cdr seqbinds)) (eq nbody (caar seqbinds)))
        (nth 1 (car seqbinds)))
       ;; General case.
       (t `(let* ,(nreverse seqbinds) ,nbody))))))