* Using variable names in a macro
@ 2017-12-09 15:49 Narendra Joshi
2017-12-10 11:28 ` Michael Heerdegen
0 siblings, 1 reply; 5+ messages in thread
From: Narendra Joshi @ 2017-12-09 15:49 UTC (permalink / raw)
To: help-gnu-emacs
Hi,
I want to refactor the following macro. I use it to do things at times
when I am idle for some time and when I come back to the keyboard
eventually. For example, showing bitcoin prices when I have been idle
for some time but removing them from the mode line once I am back as it
takes a lot of space. I think there is a lot of repetition but I am not
sure how to abstract things out into variables. It would have been
simpler if an anonymous function could refer itself.
```
(defmacro do-when-idle (f g interval)
"Call F when idle for INTERVAL seconds and then G when there is activity."
`(progn
(defun ,(intern (concat "do-when-idle-"
(sha1 (format "%s-%s" f g)))) ()
(funcall ,g)
(remove-hook 'post-command-hook
#',(intern (concat "do-when-idle-"
(sha1 (format "%s-%s" f g)))))
(run-with-idle-timer ,interval
nil
(lambda ()
(funcall ,f)
(add-hook 'post-command-hook
#',(intern (concat "do-when-idle-"
(sha1 (format "%s-%s" f g))))))))
(run-with-idle-timer ,interval
nil
(lambda ()
(funcall ,f)
(add-hook 'post-command-hook
#',(intern (concat "do-when-idle-"
(sha1 (format "%s-%s" f g)))))))))
```
Best,
--
Narendra Joshi
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Using variable names in a macro
2017-12-09 15:49 Using variable names in a macro Narendra Joshi
@ 2017-12-10 11:28 ` Michael Heerdegen
2017-12-10 14:42 ` Narendra Joshi
0 siblings, 1 reply; 5+ messages in thread
From: Michael Heerdegen @ 2017-12-10 11:28 UTC (permalink / raw)
To: Narendra Joshi; +Cc: help-gnu-emacs
Narendra Joshi <narendraj9@gmail.com> writes:
> ```
> (defmacro do-when-idle (f g interval)
> "Call F when idle for INTERVAL seconds and then G when there is activity."
> `(progn
> (defun ,(intern (concat "do-when-idle-"
> (sha1 (format "%s-%s" f g)))) ()
> (funcall ,g)
> (remove-hook 'post-command-hook
> #',(intern (concat "do-when-idle-"
> (sha1 (format "%s-%s" f g)))))
> (run-with-idle-timer ,interval
> nil
> (lambda ()
> (funcall ,f)
> (add-hook 'post-command-hook
> #',(intern (concat "do-when-idle-"
> (sha1 (format "%s-%s" f g))))))))
> (run-with-idle-timer ,interval
> nil
> (lambda ()
> (funcall ,f)
> (add-hook 'post-command-hook
> #',(intern (concat "do-when-idle-"
> (sha1 (format "%s-%s" f g)))))))))
> ```
Hmm, with some trivial simplifications of the list building, this could
look like:
#+begin_src emacs-lisp
(defmacro do-when-idle (f g interval)
"Call F when idle for INTERVAL seconds and then G when there is activity."
(let* ((name (intern (concat "do-when-idle-"
(sha1 (format "%s-%s" f g)))))
(run-idle-timer-form `(run-with-idle-timer
,interval
nil
(lambda ()
(funcall ,f)
(add-hook 'post-command-hook #',name)))))
`(progn
(defun ,name ()
(funcall ,g)
(remove-hook 'post-command-hook #',name)
,run-idle-timer-form)
,run-idle-timer-form)))
#+end_src
It would be better, though, to replace the run-idle-timer-form from with
a lambda, to avoid the code duplication.
Actually, `do-when-idle' doesn't even have to be a macro. You can make
it a defun when you define the NAME with defalias. Bind it to a closure
so that you can refer to all of the stuff with variables bound around
it.
Michael.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Using variable names in a macro
2017-12-10 11:28 ` Michael Heerdegen
@ 2017-12-10 14:42 ` Narendra Joshi
2017-12-10 17:32 ` Michael Heerdegen
0 siblings, 1 reply; 5+ messages in thread
From: Narendra Joshi @ 2017-12-10 14:42 UTC (permalink / raw)
To: Michael Heerdegen; +Cc: help-gnu-emacs
Michael Heerdegen <michael_heerdegen@web.de> writes:
> Narendra Joshi <narendraj9@gmail.com> writes:
>
>> ```
>> (defmacro do-when-idle (f g interval)
>> "Call F when idle for INTERVAL seconds and then G when there is activity."
>> `(progn
>> (defun ,(intern (concat "do-when-idle-"
>> (sha1 (format "%s-%s" f g)))) ()
>> (funcall ,g)
>> (remove-hook 'post-command-hook
>> #',(intern (concat "do-when-idle-"
>> (sha1 (format "%s-%s" f g)))))
>> (run-with-idle-timer ,interval
>> nil
>> (lambda ()
>> (funcall ,f)
>> (add-hook 'post-command-hook
>> #',(intern (concat "do-when-idle-"
>> (sha1 (format "%s-%s" f g))))))))
>> (run-with-idle-timer ,interval
>> nil
>> (lambda ()
>> (funcall ,f)
>> (add-hook 'post-command-hook
>> #',(intern (concat "do-when-idle-"
>> (sha1 (format "%s-%s" f g)))))))))
>> ```
>
> Hmm, with some trivial simplifications of the list building, this could
> look like:
>
> #+begin_src emacs-lisp
> (defmacro do-when-idle (f g interval)
> "Call F when idle for INTERVAL seconds and then G when there is activity."
> (let* ((name (intern (concat "do-when-idle-"
> (sha1 (format "%s-%s" f g)))))
> (run-idle-timer-form `(run-with-idle-timer
> ,interval
> nil
> (lambda ()
> (funcall ,f)
> (add-hook 'post-command-hook #',name)))))
> `(progn
> (defun ,name ()
> (funcall ,g)
> (remove-hook 'post-command-hook #',name)
> ,run-idle-timer-form)
> ,run-idle-timer-form)))
> #+end_src
This is a lot cleaner. Thanks! :)
> It would be better, though, to replace the run-idle-timer-form from with
> a lambda, to avoid the code duplication.
>
> Actually, `do-when-idle' doesn't even have to be a macro. You can make
> it a defun when you define the NAME with defalias. Bind it to a closure
> so that you can refer to all of the stuff with variables bound around
> it.
Can you share an example please? :)
--
Narendra Joshi
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Using variable names in a macro
2017-12-10 14:42 ` Narendra Joshi
@ 2017-12-10 17:32 ` Michael Heerdegen
2017-12-11 14:07 ` Narendra Joshi
0 siblings, 1 reply; 5+ messages in thread
From: Michael Heerdegen @ 2017-12-10 17:32 UTC (permalink / raw)
To: Narendra Joshi; +Cc: help-gnu-emacs
Narendra Joshi <narendraj9@gmail.com> writes:
> Can you share an example please? :)
If you want to go with anonymous functions, `letrec' is perfect here
(`cl-labels' would work as well and avoid the need to `funcall'):
#+begin_src emacs-lisp
;; -*- lexical-binding: t -*-
(defun my-do-when-idle (f g interval)
"Call F when idle for INTERVAL seconds and then G when there is activity."
(letrec ((run-timer-fun (lambda () (run-with-idle-timer
interval nil
(lambda ()
(funcall f)
(add-hook 'post-command-hook activity-fun)))))
(activity-fun (lambda ()
(remove-hook 'post-command-hook activity-fun)
(funcall g)
(funcall run-timer-fun))))
(funcall run-timer-fun)))
#+end_src
I've changed the order in the `activity-fun' to run `remove-hook' first,
so that you get a sane behavior when running G gives an error.
A short test:
#+begin_src emacs-lisp
(my-do-when-idle (lambda () (message "%s" (current-time-string)))
(lambda () (message "Stopping timer"))
5)
#+end_src
Michael.
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: Using variable names in a macro
2017-12-10 17:32 ` Michael Heerdegen
@ 2017-12-11 14:07 ` Narendra Joshi
0 siblings, 0 replies; 5+ messages in thread
From: Narendra Joshi @ 2017-12-11 14:07 UTC (permalink / raw)
To: Michael Heerdegen; +Cc: help-gnu-emacs
Michael Heerdegen <michael_heerdegen@web.de> writes:
> Narendra Joshi <narendraj9@gmail.com> writes:
>
>> Can you share an example please? :)
>
> If you want to go with anonymous functions, `letrec' is perfect here
> (`cl-labels' would work as well and avoid the need to `funcall'):
>
> #+begin_src emacs-lisp
> ;; -*- lexical-binding: t -*-
>
> (defun my-do-when-idle (f g interval)
> "Call F when idle for INTERVAL seconds and then G when there is activity."
> (letrec ((run-timer-fun (lambda () (run-with-idle-timer
> interval nil
> (lambda ()
> (funcall f)
> (add-hook 'post-command-hook activity-fun)))))
> (activity-fun (lambda ()
> (remove-hook 'post-command-hook activity-fun)
> (funcall g)
> (funcall run-timer-fun))))
> (funcall run-timer-fun)))
> #+end_src
Perfect. `letrec` makes it very simple! :) Thanks a lot! :)
>
> I've changed the order in the `activity-fun' to run `remove-hook' first,
> so that you get a sane behavior when running G gives an error.
Makes sense! :)
Best,
--
Narendra Joshi
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2017-12-11 14:07 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-12-09 15:49 Using variable names in a macro Narendra Joshi
2017-12-10 11:28 ` Michael Heerdegen
2017-12-10 14:42 ` Narendra Joshi
2017-12-10 17:32 ` Michael Heerdegen
2017-12-11 14:07 ` Narendra Joshi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).