2

I'd like to use a function reference in place of a lambda, however the lambda in question captures a lot of locally scoped vars declared in an enclosing let.

For example:

(let ((x 1)(y 2)(z 3)
      (l '(1 2 3 4 5 6 7 8 9)))
  (mapcar (lambda (i) (list x y z i)) l))

Let's assume the entire thing is way more complex, but is similar in essence. I'd want to move the lambda out to a defun, like this.

(defun fn (i)
  "Function to use I in context."
  (list x y z i))

(let ((x 1)(y 2)(z 3)
      (l '(1 2 3 4 5 6 7 8 9)))
  (mapcar 'fn l))

This doesn't work, x y z are void (of course.)

Is there a way to make this possible? (Assume Emacs versions which allow lexical scope.)

ocodo
  • 1,202
  • 11
  • 20

2 Answers2

3

Use flet:

(let ((x 1)(y 2)(z 3)
      (l '(1 2 3 4 5 6 7 8 9)))
  (flet ((fn (i)
             (list x y z i)))
    (mapcar #'fn l)))
db48x
  • 15,741
  • 1
  • 19
  • 23
  • Thank you! Am I correct in assuming that lexical binding is required for this to work as expected? – ocodo Sep 05 '22 at 06:06
  • 1
    No, it works fine with dynamic bindings. – db48x Sep 05 '22 at 09:06
  • Just noticed there's a pretty big problem with this. The `flet` has to be included in the let body. So the goal, which was to move the function body away somewhere else hasn't really been met. – ocodo Sep 05 '22 at 14:23
  • Still faced with an absolute monster of a let/defun block. – ocodo Sep 05 '22 at 14:24
  • 1
    It’s called lexical scope for a reason. The scope is defined by where the lexemes (as it were) of the definition are. If you want it to have access to the variables inside the function, it has to be inside the function too. – db48x Sep 05 '22 at 15:09
  • Can you suggest a way to factor the code that would meet the goal? – ocodo Sep 05 '22 at 15:34
  • 1
    What else do you want? There are a lot of general rules of thumb, such as breaking large functions into smaller, more self–contained pieces, that can help. – db48x Sep 05 '22 at 16:10
  • I was looking for a definitive answer, and I think you're saying there's no way to capture the state. I need to turn that state into something I can pass around (an alist / plist / etc.) instead, right? – ocodo Sep 06 '22 at 01:22
  • Potentially this is the answer I'm looking for https://stackoverflow.com/questions/31630226/emacs-lisp-pass-lexical-scope-as-to-a-function – ocodo Sep 06 '22 at 01:25
  • Right, if you want to capture the state as something you can pass around, you have to do it the old–fashioned way by actually passing it around as arguments. Once you’re doing that you can just make it an ordinary function, since nothing in the body needs access to the lexical environment of its definition any more. – db48x Sep 06 '22 at 08:29
1

Ok, so the answers I'm looking for are along the lines of "is this possible", i.e. can scope state be shared to a function arbitrarily.

These two cover the topic effectively.

The fact that the internal structure of a closure is "exposed" to the rest of the Lisp world is considered an internal implementation detail. For this reason, we recommend against directly examining or altering the structure of closure objects.

Effectively, the answer is NO, it can't be done (and this goes for relying on internal implementation details in general). The state in use by the lambda in this case, needs to be passed via a conventional method, i.e. parameters, a list structure, etc.

ocodo
  • 1,202
  • 11
  • 20