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.

That is besides the point.  The "callback" is just an argument to the
method, an optional argument.  For that we use an options dict. We can
check if any of the items are not supported and give an error.

It is totally different when the dict is handled like an object, in this
case a timer object.  That dict can then contain lots of items that are
not options.  We can't check if some items are invalid.

To do it properly would require defining a timer object, returned by
timer_new().  Would then have timer_set_callback() or whatever to set
options before calling timer_start().  Adding other members to the dict
should probably not be allowed.  But all this doesn't fit into how Vim
works.

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

Well, nobody said anything about that so far.  I did notice when writing
tests, but since nobody made a remark I left the dash-names.

So we should probably rename them, change dashes to underscores.

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

It's a good idea to pass the arguments in a list to function().  And use
the third argument for the dict.  Could even detect the second argument
to be a dict:
        function(name)
        function(name, dict)
        function(name, arglist)
        function(name, arglist, dict)
Can't really do this wrong.

The "bound function" won't add functionality, it's just shorter.
In a cryptic way, it's not directly clear what happens.  I prefer
keeping it explicit, using function().

-- 
Everyone has a photographic memory. Some don't have film.

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

Raspunde prin e-mail lui