unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* 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).