Function: named-let

named-let is an autoloaded macro defined in subr-x.el.gz.

Signature

(named-let NAME BINDINGS &rest BODY)

Documentation

Looping construct taken from Scheme.

Like let, bind variables in BINDINGS and then evaluate BODY, but with the twist that BODY can evaluate itself recursively by calling NAME, where the arguments passed to NAME are used as the new values of the bound variables in the recursive invocation.

This construct can only be used with lexical binding.

View in manual

Probably introduced at or before Emacs version 28.1.

Source Code

;; Defined in /usr/src/emacs/lisp/emacs-lisp/subr-x.el.gz
;;;###autoload
(defmacro named-let (name bindings &rest body)
  "Looping construct taken from Scheme.
Like `let', bind variables in BINDINGS and then evaluate BODY,
but with the twist that BODY can evaluate itself recursively by
calling NAME, where the arguments passed to NAME are used
as the new values of the bound variables in the recursive invocation.

This construct can only be used with lexical binding."
  (declare (indent 2) (debug (symbolp (&rest (symbolp form)) body)))
  (require 'cl-lib)
  (unless lexical-binding
    (error "`named-let' requires lexical binding"))
  (let ((fargs (mapcar (lambda (b) (if (consp b) (car b) b)) bindings))
        (aargs (mapcar (lambda (b) (if (consp b) (cadr b))) bindings)))
    ;; According to the Scheme semantics of named let, `name' is not in scope
    ;; while evaluating the expressions in `bindings', and for this reason, the
    ;; "initial" function call below needs to be outside of the `cl-labels'.
    ;; When the "self-tco" eliminates all recursive calls, the `cl-labels'
    ;; expands to a lambda which the byte-compiler then combines with the
    ;; funcall to make a `let' so we end up with a plain `while' loop and no
    ;; remaining `lambda' at all.
    `(funcall
      (cl-labels ((,name ,fargs . ,body)) #',name)
      . ,aargs)))