One common way to detect recursion without using a global variable is the use of an optional argument:
(defun foo (x &optional recursed)
"Frobnicate X."
(unless recursed
(foo x t)))
You could even hide the optional argument from the function's API:
(defun foo (x &optional recursed)
"Frobnicate X."
(declare (advertised-calling-convention (x) "1.0.0"))
(unless recursed
(foo x t)))
or:
(defun foo (x &optional recursed)
"Frobnicate X.
\n(fn X)"
(unless recursed
(foo x t)))
(See (info "(elisp) Function Documentation")
.)
Another way is to create a closure:
(defalias 'foo
(let (recursed)
(lambda (x)
(unless recursed
(setq recursed t)
(foo x))))
"Frobnicate X.")
Of course, there's nothing wrong with using a dynamic variable instead, depending on your needs. For example, the built-in lisp/net/shr.el
HTML renderer uses a counter variable that it increments with each recursive call to shr-descend
.
If you're looking for something more complicated than just guarding against recursion, then you may want to look at how called-interactively-p
uses backtrace-frame
, or mapbacktrace
.