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?

2
  • they are now balanced. Nov 9, 2018 at 23:10
  • Thanks, again. I double checked it this time. Nov 10, 2018 at 4:47

1 Answer 1

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))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.