0

I am having a problem working with closures. I hope I can explain this clearly. When I try to start a compilation I typically do it like this:

(let ((dir (get-dir))
  (model-root root)
  (default-directory dir)
  (compilation-environment env)
  (compilation-buffer-name-function
   (lambda (mode)
     (format "*%s %s %s*"
             (f-filename model-root)
             (downcase mode)
             (f-filename dir)))))
(compilation-start "some command" 'foo-mode))

But, I want to use a custom function instead of compilation-start. This custom function will add commands to a queue instead of running them right away. However that means I need to capture the dynamically scoped variables from the let binding. This works fine except for when I get the value of compilation-buffer-name-function, it evaluates to a closure like this.

   (closure ((model-root . "/root/path/") (dir . "/some/directory/") t)
       (mode) (format "%s %s %s" (f-filename model-root) (downcase mode) (f-filename dir)))

The problem is, that form is not a valid form for evaluation. It gives me the error let: Symbol’s function definition is void: closure. I am aware the closure is not a real function and that is merely an implementation detail that is exposed to the user.

Here is my code to call compilation-start when the command is ready (this is the code that is giving the error).

  (cl-loop for command in compilation-queued-commands
                   with bindings = (car command)
                   with args = (cdr command)
                   do (eval `(let ,bindings         
                                 (apply 'compilation-start ',(-flatten args)))))

This is the code I am using to capture the dynamic variables

(defvar compilation-dynamic-closures '(compilation-buffer-name-function
                                      compilation-environment
                                      default-directory))
(add-to-list 'compilation-queued-commands
                 (list (mapcar (lambda (var)
                                 (list var (symbol-value var)))
                               compilation-dynamic-closures)
                       compilation-args))

So my question is, how can I capture and replay this closure later?

Prgrm.celeritas
  • 849
  • 6
  • 15

1 Answers1

1

I was experimenting with the reader macros and I think I figured it out. The problem is that I am trying to evaluate, the let values twice. Once when they are first bound and then again when we rebind them. This is not a problem for variables that are strings, because strings always evaluate to themselves. But lambdas evaluate to closures, and you can’t evaluate a closure. So I need to only evaluate them once. I did this by using the reader form `', this ensures that it is not evaluated a second time. This changes my capture code to look like this.

(add-to-list 'compilation-queued-commands
             (list (mapcar (lambda (var)
                             (list var `',(symbol-value var)))
                           compilation-dynamic-closures)
                   compilation-args))
Prgrm.celeritas
  • 849
  • 6
  • 15