unofficial mirror of help-gnu-emacs@gnu.org
 help / color / mirror / Atom feed
* Timer variable binding
@ 2014-01-07 14:53 Johan Andersson
  2014-01-07 16:15 ` Nicolas Richard
  0 siblings, 1 reply; 5+ messages in thread
From: Johan Andersson @ 2014-01-07 14:53 UTC (permalink / raw)
  To: help-gnu-emacs

Hi,

I have some questions regarding timers.

This code will start a timer and the first time the callback runs, the
timer is canceled. Works great!

(let ((timer (run-at-time 0 1 (lambda ()
                                (cancel-timer timer))))))

In the above example I can access the timer variable inside the function
callback. But in this code, I cannot access the variable my-var. Nothing is
printed. In Emacs 24.3.1 I see no error, but in 24.3.50.1 I get
(void-variable my-var). Why is there no error in 24.3.1?

(let* (timer (my-var 10))
  (setq timer (run-at-time 0 1 (lambda ()
                                 (print my-var)
                                 (cancel-timer timer)))))

I can solve this by passing the my-var variable as argument to run-at-time,
but then that same value will be passed to the function callback each time.
Let's say for example that I want to run a timer 10 times, I can't do this
using the code below, because my-var will always have value 10:

(let* (timer (my-var 10))
  (setq timer (run-at-time 0 1 (lambda (my-var)
                                 (setq my-var (1- my-var))
                                 (when (= my-var 0)
                                   (cancel-timer timer)))
                           my-var)))

Something else worth noting is that in the callback function, it is
possible to access globally defined variables.

Can someone please explain these weird behaviors?


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Timer variable binding
  2014-01-07 14:53 Timer variable binding Johan Andersson
@ 2014-01-07 16:15 ` Nicolas Richard
  2014-01-07 19:52   ` Johan Andersson
  0 siblings, 1 reply; 5+ messages in thread
From: Nicolas Richard @ 2014-01-07 16:15 UTC (permalink / raw)
  To: Johan Andersson; +Cc: help-gnu-emacs

Johan Andersson <johan.rejeep@gmail.com> writes:
> I have some questions regarding timers.

To understand what's going on, you also need to understand what lexical
and dynamical binding is. See (info "(elisp) Variable Scoping")

> This code will start a timer and the first time the callback runs, the
> timer is canceled. Works great!
>
> (let ((timer (run-at-time 0 1 (lambda ()
>                                 (cancel-timer timer))))))
>
> In the above example I can access the timer variable inside the function
> callback.

Just changing the name from "timer" to "foobar" will make it complain :

(let ((foobar (run-at-time 1 1 (lambda ()
                                (cancel-timer foobar))))))

In fact you are lucky (or not, depending on the point of view) to give
your variable a symbol (namely, "timer") that is dynamically bound to
the 'current timer' at the time the callback function is being run.

I guess it happens in the function "timer-event-handler" : the argument
of that function is named "timer", and it is dynamically bound because
that file does not use lexical binding -- if it did, it'd make an error
with "timer" as well as with "foobar".

> But in this code, I cannot access the variable my-var. Nothing is
> printed. In Emacs 24.3.1 I see no error, but in 24.3.50.1 I get
> (void-variable my-var). Why is there no error in 24.3.1?

I don't know.

> (let* (timer (my-var 10))
>   (setq timer (run-at-time 0 1 (lambda ()
>                                  (print my-var)
>                                  (cancel-timer timer)))))

This now should make sense : in fact none of your "timer" or "my-var"
are seen by the callback. It's pure luck that "timer", when the lambda
is run, refers to the current timer.

Now if you run your code with lexical-binding set to 't'
(let* (timer (my-var 10))
  (setq timer (run-at-time 0 1 (lambda ()
                                 (print my-var)
                                 (cancel-timer timer)))))
It'll work as expected: the lambda now is made into a closure (i.e. a
function which knows about its current lexical environment).

Then this will work :
(let* (timer (my-var 10))
  (setq timer (run-at-time 0 1 (lambda nil
                                 (setq my-var (1- my-var))
                                 (message "my-var: %s" my-var)
                                 (when (= my-var 0)
                                   (cancel-timer timer))))))

but this won't work :

(let* (timer (my-var 10))
  (setq timer (run-at-time 0 1 (lambda (my-var)
                                 (setq my-var (1- my-var))
                                 (message "my-var: %s" my-var)
                                 (when (= my-var 0)
                                   (cancel-timer timer)))
                           my-var)))

> Can someone please explain these weird behaviors?

I hope I did ; don't hesitate to ask further.

-- 
Nicolas



^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Timer variable binding
  2014-01-07 16:15 ` Nicolas Richard
@ 2014-01-07 19:52   ` Johan Andersson
  2014-01-07 21:57     ` Nicolas Richard
  0 siblings, 1 reply; 5+ messages in thread
From: Johan Andersson @ 2014-01-07 19:52 UTC (permalink / raw)
  To: Nicolas Richard; +Cc: help-gnu-emacs

> In fact you are lucky (or not, depending on the point of view) to give
> your variable a symbol (namely, "timer") that is dynamically bound to
> the 'current timer' at the time the callback function is being run.

Of course, should have figured that out. :)

I definitely understand how this can work with lexical scoping, but I'm not
sure I understand why it does not work in with dynamic scoping. We are
wrapping the function call to run-at-time with a let statement. That should
make the variable my-var available in everything "under" it?

let (my-var=10) -> run-at-time -> *magic* -> callback-function

Looks to me like this should work? But why doesn't it? ;)


On Tue, Jan 7, 2014 at 5:15 PM, Nicolas Richard <
theonewiththeevillook@yahoo.fr> wrote:

> Johan Andersson <johan.rejeep@gmail.com> writes:
> > I have some questions regarding timers.
>
> To understand what's going on, you also need to understand what lexical
> and dynamical binding is. See (info "(elisp) Variable Scoping")
>
> > This code will start a timer and the first time the callback runs, the
> > timer is canceled. Works great!
> >
> > (let ((timer (run-at-time 0 1 (lambda ()
> >                                 (cancel-timer timer))))))
> >
> > In the above example I can access the timer variable inside the function
> > callback.
>
> Just changing the name from "timer" to "foobar" will make it complain :
>
> (let ((foobar (run-at-time 1 1 (lambda ()
>                                 (cancel-timer foobar))))))
>
> In fact you are lucky (or not, depending on the point of view) to give
> your variable a symbol (namely, "timer") that is dynamically bound to
> the 'current timer' at the time the callback function is being run.
>
> I guess it happens in the function "timer-event-handler" : the argument
> of that function is named "timer", and it is dynamically bound because
> that file does not use lexical binding -- if it did, it'd make an error
> with "timer" as well as with "foobar".
>
> > But in this code, I cannot access the variable my-var. Nothing is
> > printed. In Emacs 24.3.1 I see no error, but in 24.3.50.1 I get
> > (void-variable my-var). Why is there no error in 24.3.1?
>
> I don't know.
>
> > (let* (timer (my-var 10))
> >   (setq timer (run-at-time 0 1 (lambda ()
> >                                  (print my-var)
> >                                  (cancel-timer timer)))))
>
> This now should make sense : in fact none of your "timer" or "my-var"
> are seen by the callback. It's pure luck that "timer", when the lambda
> is run, refers to the current timer.
>
> Now if you run your code with lexical-binding set to 't'
> (let* (timer (my-var 10))
>   (setq timer (run-at-time 0 1 (lambda ()
>                                  (print my-var)
>                                  (cancel-timer timer)))))
> It'll work as expected: the lambda now is made into a closure (i.e. a
> function which knows about its current lexical environment).
>
> Then this will work :
> (let* (timer (my-var 10))
>   (setq timer (run-at-time 0 1 (lambda nil
>                                  (setq my-var (1- my-var))
>                                  (message "my-var: %s" my-var)
>                                  (when (= my-var 0)
>                                    (cancel-timer timer))))))
>
> but this won't work :
>
> (let* (timer (my-var 10))
>   (setq timer (run-at-time 0 1 (lambda (my-var)
>                                  (setq my-var (1- my-var))
>                                  (message "my-var: %s" my-var)
>                                  (when (= my-var 0)
>                                    (cancel-timer timer)))
>                            my-var)))
>
> > Can someone please explain these weird behaviors?
>
> I hope I did ; don't hesitate to ask further.
>
> --
> Nicolas
>


^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Timer variable binding
  2014-01-07 19:52   ` Johan Andersson
@ 2014-01-07 21:57     ` Nicolas Richard
  2014-01-08  6:25       ` Johan Andersson
  0 siblings, 1 reply; 5+ messages in thread
From: Nicolas Richard @ 2014-01-07 21:57 UTC (permalink / raw)
  To: Johan Andersson; +Cc: help-gnu-emacs@gnu.org

> let (my-var=10) -> run-at-time -> *magic* -> callback-function
> Looks to me like this should work? But why doesn't it? ;)

By the time the callback is called, the let form is long gone. The callback-function is stored in some place (namely, in a timer structure in the global variable timer-list) and then, after some time, another piece of emacs code calls that function. At that moment, there's no more let binding.

Why it works with lexical binding is because the lambda is made into a closure, which then knows what the symbol my-var is.

In a buffer where lexical-binding is t, you can try evalling
(setq foobar (let ((my-var 0)) (lambda () (incf my-var) (message "my-var: %s" my-var))))
the answer will be:
(closure ((my-var . 0) t) nil (setq my-var (1+ my-var)) (message "my-var: %s" my-var))
if you now call (funcall foobar), you'll get "my-var: 1"
and now, evalling foobar gives:
(closure ((my-var . 1) t) nil (setq my-var (1+ my-var)) (message "my-var: %s" my-var))
This is magic !

Doing the same without lex-bind will give you a lambda, then an error, then the same lambda.

-- 

Nico.




^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: Timer variable binding
  2014-01-07 21:57     ` Nicolas Richard
@ 2014-01-08  6:25       ` Johan Andersson
  0 siblings, 0 replies; 5+ messages in thread
From: Johan Andersson @ 2014-01-08  6:25 UTC (permalink / raw)
  To: Nicolas Richard; +Cc: help-gnu-emacs

Of course. Obvious now that you say it. Lexical binding it is. Thanks very
much for the answers!
On Jan 7, 2014 10:57 PM, "Nicolas Richard" <theonewiththeevillook@yahoo.fr>
wrote:

> > let (my-var=10) -> run-at-time -> *magic* -> callback-function
> > Looks to me like this should work? But why doesn't it? ;)
>
> By the time the callback is called, the let form is long gone. The
> callback-function is stored in some place (namely, in a timer structure in
> the global variable timer-list) and then, after some time, another piece of
> emacs code calls that function. At that moment, there's no more let binding.
>
> Why it works with lexical binding is because the lambda is made into a
> closure, which then knows what the symbol my-var is.
>
> In a buffer where lexical-binding is t, you can try evalling
> (setq foobar (let ((my-var 0)) (lambda () (incf my-var) (message "my-var:
> %s" my-var))))
> the answer will be:
> (closure ((my-var . 0) t) nil (setq my-var (1+ my-var)) (message "my-var:
> %s" my-var))
> if you now call (funcall foobar), you'll get "my-var: 1"
> and now, evalling foobar gives:
> (closure ((my-var . 1) t) nil (setq my-var (1+ my-var)) (message "my-var:
> %s" my-var))
> This is magic !
>
> Doing the same without lex-bind will give you a lambda, then an error,
> then the same lambda.
>
> --
>
> Nico.
>
>


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-01-08  6:25 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-01-07 14:53 Timer variable binding Johan Andersson
2014-01-07 16:15 ` Nicolas Richard
2014-01-07 19:52   ` Johan Andersson
2014-01-07 21:57     ` Nicolas Richard
2014-01-08  6:25       ` Johan Andersson

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).