2016-03-14 1:39 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.
>
> 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.

Underscore names are not much useful until dictionary functions can be
used as callbacks (or it will be possible to create non-dictionary
anonymous functions using `function options.out_cb()` syntax). So
people did not complain.

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

Not more cryptic as current solution is:

    let d = {}
    function d.f() dict
    endfunction
    echo d.f()  " Echoes 0
    echo (d.f)()  " E725: Calling dict function without Dictionary

This is surprising to any person coming from OO language, lua (which
also does not have “bound functions”) in the second case case (though
`(d:f)()` and not `(d.f)()` as it is not different from `d.f()`)
raises syntax error. But still I do not think bound functions are good
idea, this raises loads of questions like

    let d = {}
    function d.f() dict
        return self is d
    endfunction
    let d2 = {'f': d.f}
    echo d2.f()

: what should it print? If 1 then this is likely to break existing
plugins. And 0 is not what is expected from “bound function” call.

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