0

If I have a function which needs to know if it's calling itself, what is an idiomatic way to check for this situation?

While I can always bind a symbol (with a name that's highly likely to be unique) with let, then check if it's declared in the outer scope, I'm not sure if this is the best way.

Is there a common convention for handling this case?

2
  • emacs.stackexchange.com/tags/elisp/info
    – NickD
    Commented Dec 15, 2020 at 1:18
  • 2
    I believe @Basil answered the question. But I think it might help if you added an example use case - e.g., show why you want a function to know if its current invocation is a recursive (or an iterated) one.
    – Drew
    Commented Dec 15, 2020 at 4:42

1 Answer 1

2

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.

2
  • I can't use the calling convention method, as I don't have a lot of control of who is calling what/when. Could you show how the foo alias would be used?
    – ideasman42
    Commented Dec 15, 2020 at 2:06
  • @ideasman42 The closure is like any old defun, except recursed is nil only the first time it is called. You can try calling it for any value X as (foo X).
    – Basil
    Commented Dec 15, 2020 at 2:14

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.