2

I have a favorite function that contains several levels of set-process-sentinel and lambda sentinels. I have been using lexical-let, but would like to see if it is possible to use dynamically bound variables using a backtick / comma to penetrate several levels deep. In my testing, I see that a variable dynamically bound using a backtick / comma only pentetrates a couple of levels before the variable becomes void. Is there a way to increase the levels of penetration when using the backtick?

Example with lexical-let:

(defun example-lexical-fn ()
"Doc-string"
(interactive)
  (lexical-let* ((test-variable "Hello-world!"))
    (set-process-sentinel
      (start-process "process-one" "*one*" "echo" test-variable)
      (lambda (p e) (when (= 0 (process-exit-status p))
        (set-process-sentinel
          (start-process "process-two" "*two*" "echo" test-variable)
          (lambda (p e) (when (= 0 (process-exit-status p))
            (start-process "process-three" "*three*" "echo" test-variable)
            (set-process-sentinel
              (start-process "process-four" "*four*" "echo" test-variable)
              (lambda (p e) (when (= 0 (process-exit-status p))
                (set-process-sentinel
                  (start-process "process-five" "*five*" "echo" test-variable)
                  (lambda (p e) (when (= 0 (process-exit-status p))
                    (message "test-variable:  %s" test-variable)))))))))))))))

The dynamic binding putters-out and the test-variable becomes a void-variable at the section labeled "process-three".

(defun example-dynamic-fn ()
"Doc-string"
(interactive)
  (let* ((test-variable "Hello-world!"))
    (set-process-sentinel
      (start-process "process-one" "*one*" "echo" test-variable)
      `(lambda (p e) (when (= 0 (process-exit-status p))
        (set-process-sentinel
          (start-process "process-two" "*two*" "echo" ,test-variable)
          `(lambda (p e) (when (= 0 (process-exit-status p))
            (start-process "process-three" "*three*" "echo" ,test-variable)
            (set-process-sentinel
              (start-process "process-four" "*four*" "echo" ,test-variable)
              `(lambda (p e) (when (= 0 (process-exit-status p))
                (set-process-sentinel
                  (start-process "process-five" "*five*" "echo" ,test-variable)
                  `(lambda (p e) (when (= 0 (process-exit-status p))
                    (message "test-variable:  %s" ,test-variable)))))))))))))))

Here is the working example based on the helpful answer of zk_phi: "Change all backticks to quote (') except for the first one . . . ."

(defun example-dynamic-fn ()
"Doc-string"
(interactive)
  (let ((test-variable "Hello-world!"))
    (set-process-sentinel
      (start-process "process-one" "*one*" "echo" test-variable)
      `(lambda (p e) (when (= 0 (process-exit-status p))
        (set-process-sentinel
          (start-process "process-two" "*two*" "echo" ,test-variable)
          '(lambda (p e) (when (= 0 (process-exit-status p))
            (start-process "process-three" "*three*" "echo" ,test-variable)
            (set-process-sentinel
              (start-process "process-four" "*four*" "echo" ,test-variable)
              '(lambda (p e) (when (= 0 (process-exit-status p))
                (set-process-sentinel
                  (start-process "process-five" "*five*" "echo" ,test-variable)
                  '(lambda (p e) (when (= 0 (process-exit-status p))
                    (message "test-variable:  %s" ,test-variable)))))))))))))))
2
  • I think you should only need a single backquote. – npostavs May 29 '16 at 0:08
  • 1
    Using nested backtick instead of lexical scoping is masochism. – Stefan May 29 '16 at 15:12
1

You have too many backticks in the expression. If you nest n backticks, then you need n commas before the expression you want to splice.

highlight-stages package shows which parts in a expression are quoted (not evaluated), FYI.

3
  • Would it be possible for you to please block and copy my second example, modify it, and post it as a working solution so that I can test and verify that it works as advertised. Thanks in advance. :) Since the example uses echo as the executable, it should work on any *nix flavored operating system. The last line that is (message "test-variable: %s" . . .) should return "test-variable: Hello-world!" in the *Messages* buffer if the solution is indeed the correct answer. Much appreciated . . . – lawlist May 29 '16 at 5:17
  • 1
    Change all backticks to quote (') except for the first one, and it should work :) – zk_phi May 29 '16 at 9:18
  • Yes, that does indeed fix the second example -- thank you very much -- greatly appreciated! – lawlist May 29 '16 at 16:16
1

Using backquotes like this is a bad idea: everything within the backquote loses access to lexically-scoped variables, plus it won't be compiled, macros will be expanded late/repeatedly, kittens will suffer, your karma will go down, and then some.

Better do something like

;; -*- lexical-binding:t -*-
(defvar my-dynbound-var)

(defun example-dynamic-fn ()
  "Doc-string"
  (interactive)
  (let ((v "Hello World!"))
    (set-process-sentinel
      (start-process "process-one" "*one*" "echo" v)
      (lambda (p e)
        (let ((my-dynbound-var v))
          ...
          (set-process-sentinel
            (start-process "process-four" "*four*" "echo" v)
            (lambda (p e)
              (let ((my-dynbound-var v))
                ...))))))))

I.e. you re-bind the dynbound variable within each sentinel where you need it, and propagate the value between them via a lexically-scoped var.

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.