Topic: lambda NEW PROPOSAL (short)


Author: jarvij@gmail.com
Date: Fri, 3 Nov 2006 22:29:30 CST
Raw View
Sektor van Skijlen wrote:
> Ok, in order not to waste your time, this is my proposal, different from the
> existing and consistent with the rest of C++.
>
> 1. Syntax
>
> Having a function type like this:
>
>  typedef int filter_int( int );
>
> We can use it to define a lambda construct:
>
>  transform( v1, v2, filter_int( auto i ) { return i*2; } );
>
> Using variadic template feature, a generic function type can be created and
> added to the standard library, say, std::lambda.

Can you be more precise? I don't see how.

>
> 2. Memory management and closures
>
> State such a function:
>
> int accm( const vector<int>& v )
> {
>  int n = 0;
>  for_each( v, lambda<void>( int i ) { n += i; } );
>  return n;
> }
>
> The 'n' here is accessed via reference, because the lambda is passed also via
> reference:
>
> template<typename F, Container C >
> F for_each( C& c, F& f ) {  ... }
>
Which for_each is this? None that I have seen takes the function object
by non-const reference. That would break most uses of for_each,
including yours above.

> If the lambda is passed via value, the closure variables are also accessed via
> value.
>
Any rationale for this rule for selecting between reference/value
semantics?
Seems like a pretty random heuristic.

Cheers,

  Jaakko J   rvi


---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]





Author: Sektor van Skijlen <ethouris@guess.if.gmail.com.is.valid.or.invalid>
Date: Sun, 5 Nov 2006 00:49:33 CST
Raw View
Dnia Fri,  3 Nov 2006 22:29:30 CST, jarvij@gmail.com skrobie:
> Sektor van Skijlen wrote:
> > Ok, in order not to waste your time, this is my proposal, different from the
> > existing and consistent with the rest of C++.
> >
> > 1. Syntax
> >
> > Having a function type like this:
> >
> >       typedef int filter_int( int );
> >
> > We can use it to define a lambda construct:
> >
> >       transform( v1, v2, filter_int( auto i ) { return i*2; } );
> >
> > Using variadic template feature, a generic function type can be created and
> > added to the standard library, say, std::lambda.

> Can you be more precise? I don't see how.

Two features are needed to define it:

1. Replace typedef with "using =" and allowing templates for this statement
2. Variadic templates

AFAIK, both are already approved. So, I think, it would be something like
that:

template<typename Ret, typename... Args>
using lambda = Ret(Args...);

> > 2. Memory management and closures
> >
> > State such a function:
> >
> > int accm( const vector<int>& v )
> > {
> >       int n = 0;
> >       for_each( v, lambda<void>( int i ) { n += i; } );
> >       return n;
> > }
> >
> > The 'n' here is accessed via reference, because the lambda is passed also via
> > reference:
> >
> > template<typename F, Container C >
> > F for_each( C& c, F& f ) {  ... }
> >
> Which for_each is this? None that I have seen takes the function object
> by non-const reference. That would break most uses of for_each,
> including yours above.

Maybe I used wrong name. This does not matter; I just wanted to show my idea:
the function passed via mutable reference.

Of course, the function might be passed via immutable reference as well, but
this would simultaneously provide immutable-reference access to closure
variables, which is rather not what we want in this particular case.

> > If the lambda is passed via value, the closure variables are also accessed via
> > value.
> >
> Any rationale for this rule for selecting between reference/value
> semantics?
> Seems like a pretty random heuristic.

No :)

Think about "lambda" as about any other locally created object inside a
function. If the local function does not use any variables from the parent
function, the lambda object is empty. Otherwise these variables are part of
the lambda object contents.

This is the rationale:

There are two possible uses of lambdas:

1. Register for further callback
2. Use it in place and forget it

For #1, the lambda object should be copied, that is, a new object should be
created, which contains copies of closure variables (local variables from
parent function), simply because they will disappear once parent function
returns.

For #2, the lambda object should be accessed via reference because in this
case the user, who passes the lambda somewhere else, would like to use some
side effects, where they modify closure variables and would see the efect of
this modification.

Speaking of "who uses lambdas", we have then two roles:
1. Utility provider (someone, who wrote a function that receives the lambda to
be called inside, like for_each above)
2. Utility user (someone, who calls this utility function, defines a lambda
construct, and passes it to this utility function)

Now, consider: who should be in charge to decide, how the closure variables
should be accessed? The provider or the user? What are consequences of each
choice?

The existing proposal for C++0x states that it is up to the user to decide
about this. Effect: the user must strictly follow the documentation of the
utility function, they might make a mistake resulting in undefined
behavior(!), mostly impossible to detect by the compiler.

My proposal states that this decision is up to the provider by a simple rule:
if the provider wants to call this function immediately and do nothing else
with it - they will make the lambda received via mutable reference. They know,
what they do, moreover, trying to store this reference somewhere else is
exactly the same mistake as store a reference to a local variable. If,
however, the provider wants to store this lambda for further callback, they
will take this via value, thus making a copy of every closure variable.

This way, the user only passes the lambda and does not have to worry about
anything else. Of course, some stupid mistakes still might be made, but
require a hard work and some special tricks (for example, a user creates a
reference variable to another local variable and will use this reference
inside lambda), which eliminates the possibility of making it "just by a
mistake".

This is also CONSISTENT with the rest of C++: the lambda object can be treated
exactly like any other local variable (to be more strict: as a temporary
object) and all C++ rules applying to local variables and their cases of
memory management, referencing, copying etc., apply to the lambda object as
well. The effect is that if a compiler is able to detect any invalid uses of
local variables (in particular concerning inappropriate referencing), it would
do this for incorrect uses of lambda as well. And if a user makes a "simple
mistake" (for example, they pass a lambda to a function that will pass it
somewhere else, and expects that when it is called, it will modify some local
variable), the only effect is that the program does not work exactly as the
user expected - this is acceptible because even if undetectable by the
compiler, it will not cause undefined behavior.


Regards,

--
//  _    ___         Michal "Sektor" Malecki <sektor(whirl)kis.p.lodz.pl>
\\ L_ |/ `|  /^\ ,()                         <ethouris(O)gmail.com>
// \_ |\  \/ \_/ /\ C++ bez cholesterolu: http://www.intercon.pl/~sektor/cbx
"Java is answer for a question that has never been stated"

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html                      ]