Live data from Hacker News

Lambda expression comparison between C++11, C++14 and C++17

maitesin.github.io

81–87 of 87 posts

Re: Lambda expression comparison between C++11, C++14 and C++17

#81

Earlier quoted context omitted.

You only talk about the memory management part and I guess most language designers think the same. What you and they fail to account for is that explicit capture list can reduce logical bugs. For one, if I were allowed to explicitly capture the counter variable by copy, the surprising behavior mentioned above would never occur. In languages with mutability, the ability to make some part immutable is a virtue. For two…

> You only talk about the memory management part and I guess most language designers think the same. What you and they fail to account for is that explicit capture list can reduce logical bugs. I suppose, as a language designer, I tend to think that the more I do automatically, the more I ease the programmer's burden. However, as you point out, that's not always true. That said, my point wasn't (isn't?) that explicit…

> On the flip side, an optional capture list could be a good compromise.

That is exactly what I am thinking about. Or, rather, what C++ has done right: You can let the compiler infer what to capture, like [=] or [&], or you can explicitly list the variables to capture.

> you need the ability to specify that the "captured" variable ought to be copied rather than actually captured

Yes, that is what I am talking about, and again, what C++ has done right. Most other languages give you no choice whether the capture is by copy or by reference.

Re: Lambda expression comparison between C++11, C++14 and C++17

#82
post #62

Earlier quoted context omitted.

I'm not familiar with closures from other languages. What about capturing a shared_ptr by value?

The thing to keep in mind is that copying a shared_ptr isn't cheap at all. It's a class with a pointer and atomic reference count inside and the atomic inc/dec takes many cycles.

How does this compare to the cost of a closure in other languages? Yeah atomic reference counts are not cheap, but basically that's the point of a shared_ptr.

Re: Lambda expression comparison between C++11, C++14 and C++17

#83
One aspect of C++ lambdas that I really don’t like is the visual confusion caused by allowing "return", since at a glance this seems to affect the parent function. I have already found myself adding comments inside lambdas like "return x; // return-from-lambda" to make sure that I see what is really happening. Python by contrast does two things better: Python makes it really hard to write long expressions as lambdas, and no "return" is used in a Python "lambda". Of course, Python also allows "def" inside a "def" as a convenient way to write longer one-time functions.

I also found that while I could use C++ lambdas for things like iteration, e.g. "object->forEachThing([](Thing const& t, bool& stop){ ... })", this makes the keyword problem worse. In this type of call, if I want to implement something that is logically like a "break" or "continue" of the loop, it has to use the "return" keyword (from the lambda only) with special conditions attached such as a "bool" variable to request the break. And that is confusing to read, even though conceptually it is similar to the Objective-C NSArray "enumerateObjectsUsingBlock:" that takes a similar approach (in that the block takes a "stop" argument).

Re: Lambda expression comparison between C++11, C++14 and C++17

#84

Earlier quoted context omitted.

> You only talk about the memory management part and I guess most language designers think the same. What you and they fail to account for is that explicit capture list can reduce logical bugs. I suppose, as a language designer, I tend to think that the more I do automatically, the more I ease the programmer's burden. However, as you point out, that's not always true. That said, my point wasn't (isn't?) that explicit…

> On the flip side, an optional capture list could be a good compromise. That is exactly what I am thinking about. Or, rather, what C++ has done right: You can let the compiler infer what to capture, like [=] or [&], or you can explicitly list the variables to capture. > you need the ability to specify that the "captured" variable ought to be copied rather than actually captured Yes, that is what I am talking about,…

Ah, then I see we're in agreement :)

> Most other languages give you no choice whether the capture is by copy or by reference.

That's because in languages that have traditionally had GC (i.e., languages in the Lisp tradition or in the ML tradition), the distinction didn't matter. Those languages did not "suffer" from a value/reference dichotomy (e.g., in Scheme, you're literally capturing the variable rather than a copy or reference to the value stored within -- under the hood, that variable might always store a reference for convenience, or it might store a value for performance, but it doesn't matter as it's strictly an implementation detail).

I'm glad that the C++ committee didn't just dump closures into the language without considering this sort of interaction with other aspects of the language. Without the capture lists, closures in C++ have the potential to really suck. That the explicit capture lists even exist is evidence that they've carefully considered how the new features are going to play with existing characteristics of C++. Kudos to them for that!

Re: Lambda expression comparison between C++11, C++14 and C++17

#85

Earlier quoted context omitted.

You can use C# fat arrow syntax, it even allows removing the braces for single expression lambdas which is the most common from anyway.

> removing the braces If I have learned anything over the years, it's that removing the braces anywhere is the introduction of a bug during a rewrite waiting to happen.

It really works without any issues in C# from my experience.

Statemends like :

    list.Where(e => e.Property == ExpectedValue).Select(e => e.Property)
is much cleaner than something like :

    list.Where([](auto e) { return e.Property == ExpectedValue; }).Select([](auto e) { return e.Property; });
Braces and capture declaration adds 0 value here and it's ~80% of the use cases I see for lambdas.

Re: Lambda expression comparison between C++11, C++14 and C++17

#86
post #82

Earlier quoted context omitted.

The thing to keep in mind is that copying a shared_ptr isn't cheap at all. It's a class with a pointer and atomic reference count inside and the atomic inc/dec takes many cycles.

How does this compare to the cost of a closure in other languages? Yeah atomic reference counts are not cheap, but basically that's the point of a shared_ptr.

I'm not sure. I don't program in C++ because I want it to have performance comparable to other languages.

Re: Lambda expression comparison between C++11, C++14 and C++17

#87
post #39
post #37

Earlier quoted context omitted.

What would you propose as the syntax?

Apple extended C with block closures years ago. int b = 0; ^(int a) { return a*b; } The declaration for lambda variables is almost identical to function pointers, just with a ^ instead of a *, so there's nothing to learn (or unlearn, like C++ forces you to). The ^ looks like a lambda, and historically the lambda of lambda calculus actually was a caret accent over the variable. The argument list can be elided.

Caret as the embryonic form of lambda is apparently a myth propagated by Barendregt, and lambda is just a random Greek letter to go with alpha, beta, and eta.

http://researchblogs.cs.bham.ac.uk/thelablunch/2016/05/why-i...

Post reply on HN