Nikolay Pavlov wrote:

> >> > I have mixed feelings about this implementation.  This needs more
> >> > thoughts and discussion.
> >> >
> >> >
> >> > One of the problems with the current use of a string for an expression,
> >> > as it's passed to map(), filter(), etc., is that this is a string, which
> >> > requires taking care of quotes.
> >> >
> >> > Using lambda() for that doesn't have an advantage, it's only longer:
> >> >
> >> >         :call map(mylist, '"> " . v:val . " <"')
> >> >         :call map(mylist, lambda('"> " . v:val . " <"'))
> >> >
> >> > Perhaps we can define the lambda not as a function call, but as an
> >> > operator.  Then it will be parsed differently and we don't need to put
> >> > the expression in quotes.  We do want to be able to define it inline, as
> >> > a function argument.  We do need something around it, so that it's clear
> >> > where the end is.  Using {} would work, it's similar to a statement
> >> > block in most languages.
> >> >
> >> > We would also like to specify arguments.  We could separate the
> >> > arguments from the statements with a "gives" symbol.  That could be ->.
> >> > This avoid the strange use of v:val and v:key to pass values to the
> >> > expression, like map() does.
> >> >
> >> >         call Func(arg, lambda{v -> return v * 3.12})
> >> >
> >> > That way we can do this:
> >> >         call map(mylist, lambda{v -> return "> " . v . " <"})
> >> >
> >> > In most cases only one statement is needed, but having a few more should
> >> > be possible.  Using | to separate statements hopefully works:
> >> >
> >> >         call Func(arg, lambda{ v -> if v < 0 | return v * 3.12 | else | 
> >> > return v * -3.12 | endif})
> >> >
> >> > Something like that.  If it gets too long it's better to just define a
> >> > function.
> >>
> >> `lambda{ v -> if v < 0 | return v * 3.12 | else | return v * -3.12 |
> >> endif}` is too verbose. AFAIR I already suggested lambdas
> >> implementation once and it was just
> >>
> >>     \arg1, arg2 : expr1
> >>
> >> . I.e. lambda functions do *not* allow to use commands, only
> >> expressions. Documentation for my attempt was
> >
> > Yes, that is an option.  It does restrict the situations when it can be
> > used, but since a multi-statement lambda quickly becomes unreadable,
> > there may not be much use for it anyway.
> >
> > It does require the possibility to create a closure with a nested
> > function, in case we do need multiple statements, but if we do support
> > closures it should be done for both lambda and normally defined
> > functions anyway.
> >
> > I do not like the cryptic syntax.  I think the word "lambda" is good,
> > unless we can think of something that is easy to recognize.
> > One possible option is to focus on using the "gives" token:
> >
> >         { a1, a2 -> a1 + a2 }
> >
> > Even without an argument it can still be recognized:
> >
> >         { -> string(Today()) }
> 
> This is way harder to implement, `{` already defines a dictionary and
> it may also be used for variable names, what already requires a “call
> eval1 twice” hack. `\` has no such problem, and I do not see this too
> cryptic. If you think that `->` is necessary, it is better to prefer
> `\args -> expr1` over `\args : expr1`, not `{ args -> expr1 }`.
> 
> Prefixing lambda with `lambda` keyword makes this easy, but way too verbose.

I do think that the backslash (or any other special character that might
be available) makes it ugly.

Although we can find the end of the expression without the "}", mistakes
will be very hard to figure out.  Also reading back a lambda that's
passed as an agument to a function with many arguments will make the {}
part easy to spot.  I have done this in Zimbu and I'm happy with how it
looks.

I don't think it's hard to implement: When finding a "{" look forward,
skipping over [a-zA-Z0-9 ], if "->" is found it's a lambda.

> >> |  +lambda                                                      
> >> *expr-lambda*
> >> |  +------
> >> |  +\arg1, ...: expr1   lambda function
> >> |  +
> >> |  +Lambda functions are the lightweight variant of |user-functions|.
> >> Differences
> >> |  +between them are:
> >> |  +
> >> |  +1. Body of the lambda function is an |expr1| and not a sequence of |Ex|
> >> |  +   commands.
> >> |  +2. Lambda function does not have its own scope dictionary. It does
> >> have scope
> >> |  +   dictionary for |a:var| though. |l:| scope dictionary is
> >> inherited from the
> >> |  +   place where lambda was defined (thus it is either |g:| scope
> >> dictionary or
> >> |  +   |l:| dictionary of the function that defined the lambda).
> >> |  +   You see, it is poor man closure.
> >> |  +3. If |a:var| is not found in lambda own scope dictionary it is 
> >> searched in
> >> |  +   |a:| scope dictionary of the function where lambda was defined (if 
> >> any).
> >> |  +   This does not apply to |a:000|, |a:0|, |a:1|, ... variables.
> >> |  +4. There are no dictionary lambdas.
> >> |  +5. Lambda is not globally recorded anywhere like |anonymous-function|.
> >> |  +
> >> |  +NOTE: lambda function records the whole scope dictionary in its
> >> body and also
> >> |  +      |a:| dictionary. That means that extensive generation of lambdas 
> >> and
> >> |  +      exporting them outside of generator function may lead to a huge 
> >> memory
> >> |  +      overhead.
> >>
> >> and point 5. here required my extended-funcref branch.
> >>
> >> I do not really see any sense in allowing commands in lambdas, in
> >> cases when commands are really needed lambdas should become
> >> unreadable. Your example in my syntax is simply
> >>
> >>     \v: (a:v < 0 ? a:v * 3.12 : a:v * -3.12)
> >
> > Yes, for some places a ternary expression works.  However, Vim doesn't
> > have an expression with side effects.  Well, except when calling a
> > function, but that defeats the idea of the lambda.
> >
> > Theoretically we could use ";" to separate expressions (like the comma
> > operator in C), but I wonder we should actually do this:
> >
> >         { a -> tmp = a * 5; [tmp, tmp + 2, tmp + 5] }
> >
> > In this context it is useful, but in other places it's an invitation to
> > write confusing code.
> 
> If I absolutely want to write lambda with a variable defined inside
> and not a regular function I use
> 
>     \a -> ((\tmp -> [a, a + 2, a + 5])(a * 5))

Hmm, I don't like that at all.  It's hiding the intention.

> , this needs absolutely no extensions to expression syntax should it
> define lambdas (and I really used this in Python). Such things are
> also a reason why `extended-funcref` was required: the more plugins
> and the longer Vim session lives, the more likely that anonymous
> functions counter gets overflown. My `extended-funcref` removed global
> registry which contained *all* functions: specifically lambdas in
> extended-funcref-lambdas preview and regular anonymous functions from
> the main feature branch no longer lived with other user functions:
> they were connected to funcref only and nothing else, no global name.

What patch are you referring to?

> >> > The implementation also takes items from the context, thus creating a
> >> > closure.  I don't think that should be specific to a lambda, defining a
> >> > function inside another function should be able to do the same thing.
> >> > After all, a lambda is just a short way of defining a nameless function.
> >> >
> >> > In the implementation it seems the dictionary storing the function-local
> >> > variables is kept for a very long time.  This relies on the garbage
> >> > collector.  It's better to use reference counting to be able to free the
> >> > dictionary as soon as it's unused.
> >> >
> >> > Also, the lambda always keeps the function-local variable dict, even
> >> > when it's not actually used.  That makes lambdas a expensive.
> >> > It would be better to explicitly state the lambda is using its context.
> >> > Then we can also do that with ":function", so that we are not forced to
> >> > use a lambda if we want a closure.
> >>
> >> Note that my variant did not avoid these problems: I simply stored a
> >> whole funccall structure in addition to storing pointers to in-lambdas
> >> l:.
> >>
> >> If there was VimL parser I would suggest to determine which variables
> >> are “captured” from the outer scope, AFAIR Python does something like
> >> this. But VimL has no parser… though evalX probably may be modified to
> >> do this when skipping, with Ex commands this would be harder. There is
> >> another possible solution: explicitly define which variables from the
> >> outer scope are used: e.g.
> >>
> >>     function Foo(arg1, arg2)
> >>         let l = 42
> >>         function Bar(arg1, <a:arg2, <l)
> >>             return [a:arg1, a:arg2, l]
> >>         endfunction
> >>         echo Bar(45)
> >>     endfunction
> >>     call Foo(47, 43)
> >>     " Echoes [45, 43, 42]
> >>
> >> In this case ellipsis for varargs functions should be the last entity
> >> before the first `<…` argument, for lambdas syntax is the same.
> >
> > Specifying which variables from the environment are used makes it a lot
> > more efficient, but it can also be a hassle.  A simpler, brute-force way
> > is to specify that the function (or lambda) is a closure.  It would only
> > use variables from the context that it's in, not a level up.  So keeping
> > a reference to the dict with the local variables.
> >
> > Since it's also possible to bind a function to a dict, and use that dict
> > to store any state, I think that this is actually not all that important
> > to add.  Perhaps we should first have a good example where a closure is
> > required or much nicer than a partial bound to a dict.
> >
> > So, of the three things that the CL was split up into:
> > 1. pass function to filter() and map()
> > 2. implement lambda
> > 3. implement closure
> >
> > The first is done.  Implementing lambda seems useful enough.  I'm not so
> > sure about the need for supporting closure.
> 
> I have another impression: lambdas are mostly useful for sort(),
> map()/filter() already accept fine strings and though lambdas may be
> useful to convert `map(l, 'map(…)')` into something that contains less
> quoting madness, such constructs exist purely for optimization* and I
> am not sure that lambdas fit in here. No idea where @mattn is going to
> find real-world one-line callbacks, I do not think lambdas for
> timers/jobs/… are useful anywhere else, but in code that tests them.
> 
> On the other hand closures are needed to not bother oneself with
> copying variables into self dictionary, partial argument or whatever,
> it is more convenient to just write nested :function and access
> variables right away then first create a dictionary for this function,
> put all needed variables there and use `self.varname`. Lambdas with
> closures are more useful then without them: it is not unusual for me
> to write something like `map(l, 'add(l2, v:val)')` and obviously I
> cannot switch this code to use lambdas if they are not closures (as it
> will look like `map(l, function(\l2, v -> add(l2, v), [l2]))` which is
> rather lengthy).
> 
> * Main VimL optimization principle: the less Ex commands, the faster
> your code runs; I have a plugin where the whole :while cycle
> consisting of :if’s, :let’s (and, probably, :call’s) was squashed into
> a single :while condition, turning into `:while
> {very-very-long-condition}|endwhile`. Bad I did not yet lay down my
> hands on making this squashing automatic (cycle in question is a part
> of the generated function which is already subject to some
> optimizations).

To make it faster we would need a just-in-time compiler of some kind.
Or rewrite it in Python.  But it's unlikely that this happens.
Usually, only a very small part of the code needs to be optimized.

Although closures can make code compact (and thus less time spent on
parsing), it also introduces overhead, thus I'm not sure if you end up
with something that's actually faster.

There is also the goal to keep Vim scripting as simple as possible, only
add features that are really useful, and not make new features cryptic.

-- 
hundred-and-one symptoms of being an internet addict:
215. Your mouse-clicking forearm rivals Popeye's.

 /// Bram Moolenaar -- b...@moolenaar.net -- 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 vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui