2

Why do I get Error running timer: (void-variable message) in the function below in my `init.el - Emacs?

(defun cypher/cowsayx-sclock (in-minutes message)
  (interactive "nSet the time from now - min.: \nsWhat: ")
  (run-at-time (* in-minutes 60)
               nil
               (lambda ()
                 (message "%S" message)
                 (shell-command (format "xcowsay %s" (shell-quote-argument
                                                      message))))))
Drew
  • 29,895
  • 7
  • 74
  • 104
Achylles
  • 31
  • 4

1 Answers1

2

You need to turn on lexical-binding, for that message occurrence in the lambda not to be treated as a free variable. It's a lexical variable local to function cypher/cowsayx-sclock, but within the lambda it's free.

Otherwise, you need to instead substitute the value of variable message in the lambda expression, and use that as a list. Here's a backquoted expression that gives you that list with the message value substituted.

`(lambda ()
   (message "%S" ',message)
   (shell-command (format "xcowsay %s" (shell-quote-argument ',message)))

But this is less performant than using lexical-binding, which produces a closure for the lambda, encapsulating the value of message.

See the Elisp manual, node Using Lexical Binding.

You can, for example, just put this at the end of a comment line as the first line of your file:

  -*- lexical-binding:t -*-

For example, if your code is in file foo.el then this could be its first line:

;;; foo.el --- Code that does foo things.   -*- lexical-binding:t -*-
Drew
  • 29,895
  • 7
  • 74
  • 104
  • Worked beautifully... I still need to study the proper way you suggested here: "You need to turn on lexical-binding, for that message occurrence in the lambda not to be treated as a free variable..." – Achylles Sep 06 '21 at 01:46
  • I've added an example of setting `lexical-binding` to `t` for the scope of a given file. That's the easiest way to take care of this, typically. – Drew Sep 06 '21 at 02:49
  • If the answer is acceptable you can "accept" it. You can of course instead wait for a better answer. ;-) – Drew Sep 06 '21 at 02:49
  • Where is the accept button? I cannot see it. The answer is really great! Worked beautifully, as I said before... – Achylles Sep 07 '21 at 18:12
  • You can click on the big gray check button to the left of the answer. – Drew Sep 07 '21 at 22:03
  • Ok. But it is still confusing for people to start using stackoverflow. The devs should make thinks more friendly to normal users... There should be a legend or clear explanation or a video showing how to use all these buttons... – Achylles Sep 08 '21 at 18:10