2016-03-13 23:08 GMT+03:00 Bram Moolenaar <[email protected]>:
>
> Nikolay Pavlov wrote:
>
>> >> 2016-03-13 18:06 GMT+03:00 Bram Moolenaar <[email protected]>:
>> >> >
>> >> > I have a note about adding a way that a callback can be invoked at a
>> >> > certain time. I would like to know how this is going to be used, what
>> >> > the requirements for this feature are.
>> >> >
>> >> > I imagine there are two kinds of timers:
>> >> >
>> >> > 1. One-time: After N msec the callback is invoked.
>> >> > 2. Repating: invoke the callback every N msec
>> >> >
>> >> > For both this will happen while waiting for a character, thus the actual
>> >> > wait time will be longer if the user is typing.
>> >> >
>> >> > Example:
>> >> >
>> >> > let timer = timer_add({time}, {callback} [, {repeats} [,
>> >> > {cookie}]])
>> >> > call timer_stop(timer)
>> >> >
>> >> > func MyTimerCallback(timer, cookie)
>> >> >
>> >> > Would that satisfy the need?
>> >> >
>> >> > I think it would be good that if the callback causes an error the timer
>> >> > is stopped.
>> >>
>> >> I would say that dictionary is better then four arguments:
>> >>
>> >> 1. It enables feature used by Neovim for callbacks: dictionary that
>> >> defined timer is passed as a `self` dictionary to a dictionary
>> >> function, while in your variant dictionary functions cannot be used at
>> >> all.
>> >> 2. This allows adding more options in the future. E.g. alter error
>> >> handling, silence function or add a timeout after which callback is
>> >> interrupted.
>> >> 3. Dictionary may serve both as `timer` and as a `cookie`.
>> >>
>> >> E.g.
>> >>
>> >> let def = {
>> >> \ 'interval': Number|Float (seconds),
>> >> \ 'callback': String|Funcref,
>> >> \ 'repeats': Number (optional),
>> >> \ 'Cookie': … (keys matching `^[a-z]\+$` are reserved for Vim)}
>> >> call timer_add(def)
>> >> call timer_stop(def)
>> >>
>> >> function Callback() dict
>> >> endfunction
>> >
>> > That's very different from how existing functions work.
>> >
>> > The "cookie" can be a dictionary, thus one can use that. Passing the
>> > function argument back in to the callback looks weird. What we would
>> > actually want is a closure. Then the cookie is not needed.
>>
>> Argument being passed is a dictionary containing function, not the
>> function. And this does not look weird at all, every OOP library
>> written in VimL I saw does the same thing.
>
> What is weird is that a function is needed and you pass a dictionary
> containing a function. The only reason Vim users have been doing this
> is that there was no other way. It's not that this is a good way to do
> it.
>
> In this case the "callback" item of the dictionary defines the function.
> Why not another? What if there are there several functions? Anyway,
> I think it's weird.
Why somebody may think that there should be another item? Persons that
do not understand that {callback} should be attached to `'callback'`
key will as well not understand that {repeat} should be attached to
`'repeat'` key.
Also I did not see any problems with developers failing to understand
that ch_sendexpr() or job_start() should have handler attached to
`'callback'` key. This argument is nonsense to hear, I am not the one
that suggested to have {options} with `'callback'`, except that there
it is optional.
Several functions are absolutely fine, one may simply pass a “class
instance” as a callback.
// As a side note: why did you choose callback names so that they
cannot be called using dot-subscript notation (i.e.
`options.out_cb()`)? It is not uncommon when callback function is
reused for purposes other then being a callback, though I guess nobody
currently cares because one cannot use dictionary functions for
callbacks there, so writing `function options.out_cb(...)` is not
possible.
>
> Another objection to using the dictionary both for options and for
> passing to the callback is that it's not clear what happens if some of
> the option values is changed. Does that mean the changed value will be
> used?
This should be documented. In case of a repeating execution in some
cases it may actually be convenient to adjust interval by assigning
new value to `'interval'` key. And restarting is in any case easier if
everything is kept in one container:
```
let timer = {'interval': 1, 'Cookie': []}
function timer.callback() dict
call add(self.Cookie, 'test')
endfunction
function timer.enable() dict
call timer_add(self) " Should do nothing if timer was enabled
because dictionary is an identifier
endfunction
function timer.disable() dict
call timer_del(self)
endfunction
```
vs
```
function Callback(cookie)
call add(a:cookie, 'test')
endfunction
let g:timer = v:null
let g:cookie = []
function EnableTimer()
if g:timer is v:null " Needed because new timer_add will simply
make function run twice, callback is not an identifier
let g:timer = timer_add(1, function('Callback', [g:cookie]))
endif
endfunction
function DisableTimer()
call timer_del(g:timer)
let g:timer = v:null
endfunction
```
Using dictionary functions does not make much difference in the second case.
>
> I think it's better to separate options from any dictionary related to
> the callback. And there are many other places callbacks are used where
> binding it with arguments or a dictionary is useful.
Partial application is orthogonal to timer_* API. It is useful even
without any callbacks involved.
>
>> > We could make this generic:
>> > let closure = function('Callback', arg1, arg2)
>> > Would invoke:
>> > func Callback(arg1, arg2, other-args)
>>
>> This is currying (partial application), not closure. Closure is
>>
>> function Outer()
>> let l:test = 1
>> let l:d = {}
>> function d.inner()
>> return l:test
>> endfunction
>> return d.inner
>> endfunction
>>
>> let Closure = Outer()
>> echo Closure()
>
> Yeah, I know it's not a real closure. But it's the best term that I
> though of. Never heard of currying. Looking it up finds something
> else... Perhaps "partial" is a better fit. Not sure how many people
> know what that means.
>
>> > We might need more options later, that's true. But the time and
>> > callback are always needed. Thus I would prefer:
>> >
>> > let timer = timer_start({time}, {callback}, {options})
>>
>> Do you mean that `options` here will be `self`? It is fine as long as
>> dictionary functions can be used.
>
> No, they are options. If a dictionary function is to be used we need a
> way that works for any callback. Perhaps:
>
> let callback = function('Callback', dict, arg1, arg2)
>
> Nicer would be if we can do:
>
> let callback = function(dict.Callback, arg1, arg2)
I would suggest function(name[, args[, self]]) instead where `args` is
a list. Otherwise you need to change `dict.Callback` expression to
produce “bound function”. It is not uncommon thing in OO languages,
but I do not remember this anywhere else.
E.g. in Python
s = 'test'
rep = s.replace
print(rep('e', ''))
will show `tst` because `s.replace` is a bound method (`print(rep)`
produces `<built-in method replace of str object at 0x7f0ad2ebac70>`
in Python 3 where 0x7f0ad2ebac70 is address of object to which `rep`
is bound).
Do not forget that bound functions need to hold references of the
object they are bound to.
>
> --
> Micro$oft: where do you want to go today?
> Linux: where do you want to go tomorrow?
> FreeBSD: are you guys coming, or what?
>
> /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
> /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
> \\\ an exciting new programming language -- http://www.Zimbu.org ///
> \\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.