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, 2016 at 0:08
  • 1
    Using nested backtick instead of lexical scoping is masochism.
    – Stefan
    May 29, 2016 at 15:12

2 Answers 2

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, 2016 at 5:17
  • 1
    Change all backticks to quote (') except for the first one, and it should work :)
    – zk_phi
    May 29, 2016 at 9:18
  • Yes, that does indeed fix the second example -- thank you very much -- greatly appreciated!
    – lawlist
    May 29, 2016 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 and acknowledge that you have read and understand our privacy policy and code of conduct.

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