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.
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))))))