Live data from Hacker News

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

maitesin.github.io

41–50 of 87 posts

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

#41
Not a bad article. I wish the first example wasn't so complicated. C++ lambda syntax is pretty gross. The initial breakdown is great. But why use a std::vector and std::transform in the first real example? Stick to integer addition. Keep things simple.

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

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

Looking here (https://developer.apple.com/library/ios/documentation/Cocoa/...) for the details, I think this is going to largely be a matter of opinion. I prefer the C++ syntax, especially when it comes to capture by reference, for which the Apple syntax seems to require the __block storage type modifier.

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

#43

Earlier quoted context omitted.

I was burned by the same thing in Javascript. "Explicit is better than implicit". Therefore, I agree with you that explicit closure list, with the ability to copy and reference captured variables, is actually what C++ does right, not wrong.

The explicit capture list is only necessary in C++ because memory ownership and lifetimes are managed by the programmer in C++. Compare that to a garbage-collected language like Scheme or C#: when the implementation can figure out where memory needs to be freed and ensures you can't use-after-free, it frees the programmer from thinking about ownership (but not necessarily lifetimes: you can still wind up with memory…

It was enough of a problem in Scala to warrant this though http://docs.scala-lang.org/sips/pending/spores.html

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

#44
post #13

Earlier quoted context omitted.

How is it ugly? The capture list is a necessary complexity in a language with manual memory management.

The syntax is not the prettiest, but it is legible once you understand what [](){} means. In C#, there is no such thing, but there is a part of me that wishes we had such a thing. I like the ability explicitly state what variables are being captured.

I'm not 100% sure, but C#'s compiler should automatically capture what you need (and leave out the rest).

I think the primary need for manual declaration is because in C++ you need to differentiate between pass by copy semantics and pass by reference semantics.

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

#45
post #44

Earlier quoted context omitted.

The syntax is not the prettiest, but it is legible once you understand what [](){} means. In C#, there is no such thing, but there is a part of me that wishes we had such a thing. I like the ability explicitly state what variables are being captured.

I'm not 100% sure, but C#'s compiler should automatically capture what you need (and leave out the rest). I think the primary need for manual declaration is because in C++ you need to differentiate between pass by copy semantics and pass by reference semantics.

Yep, in Microsoft’s C# compiler, only the closed over local variables of a function are captured (which, in C#’s case, means generating a class with fields corresponding to each closed over local, and then replacing those locals with references to their respective fields of an instance of that class).

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

#46

Earlier quoted context omitted.

I was burned by the same thing in Javascript. "Explicit is better than implicit". Therefore, I agree with you that explicit closure list, with the ability to copy and reference captured variables, is actually what C++ does right, not wrong.

The explicit capture list is only necessary in C++ because memory ownership and lifetimes are managed by the programmer in C++. Compare that to a garbage-collected language like Scheme or C#: when the implementation can figure out where memory needs to be freed and ensures you can't use-after-free, it frees the programmer from thinking about ownership (but not necessarily lifetimes: you can still wind up with memory…

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, in languages without explicit variable declaration, which variable is defined where quickly becomes murky when you have implicit capture. I have so many frustrations where the inner `i` variable clashes with the outer `i` in Python. Yes, I could just use a different name, but naming is hard, and with a new scope I should be able to reuse the name. That is almost the whole point of opening a new scope!

For three, in Javascript where closures are everywhere due to the amount of callbacks, the reference graph is just impossible to analyze. A closure may closes over another closure which closes over an object with a reference to the original closure. An explicit capture list makes the programmer think, and ease the job of anyone who tries to spot memory leaks from the source code. (But I guess that is just not the Javascript style, as they are so fond of never letting the programmers know about their mistakes. At least in C++ we trade that for speed. I don't know what Javascript trades that for.)

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

#48

Earlier quoted context omitted.

The explicit capture list is only necessary in C++ because memory ownership and lifetimes are managed by the programmer in C++. Compare that to a garbage-collected language like Scheme or C#: when the implementation can figure out where memory needs to be freed and ensures you can't use-after-free, it frees the programmer from thinking about ownership (but not necessarily lifetimes: you can still wind up with memory…

That is almost true, but there's one exception in those GC'ed languages due to the dichotomy of value types and reference types. The confusing behavior on capturing the iteration variable is one example.

Ah, yes! You're correct. I spend most of my GC'd time in languages that don't have such a value vs. reference dichotomy, and I'd completely forgotten about it.

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

#49
post #10

Taking a concept such as a lambda function and making it look this ugly...this is why I hate C++. I wish I wasn't forced to program it every day.

Agreed. From the template library on down, it seems like the C++ community is hellbent on making the syntax for what should be clean, common operations seem like arcane Sanskrit. I dont know what their problem is.

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

#50

Earlier quoted context omitted.

The explicit capture list is only necessary in C++ because memory ownership and lifetimes are managed by the programmer in C++. Compare that to a garbage-collected language like Scheme or C#: when the implementation can figure out where memory needs to be freed and ensures you can't use-after-free, it frees the programmer from thinking about ownership (but not necessarily lifetimes: you can still wind up with memory…

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 capture is only a good idea sans automatic memory management (it may well be -- you've certainly given me some food for thought here), but rather that it's only necessary in that case, and I think that point still stands.

> For one, if I were allowed to explicitly capture the counter variable by copy, the surprising behavior mentioned above would never occur.

That's a failure of language design and I don't think the proper solution is to force explicit capture on closure creation (also note that you need more than just explicit capture because to prevent such an error, you need the ability to specify that the "captured" variable ought to be copied rather than actually captured). I think the proper solution to that problem is the one that the C# team went with: limit the scope of iteration control variables to the iterated block. This is typically what programmers used to block-structured languages would expect, anyway, unless the variable were clearly declared outside the scope of the iteration.

> In languages with mutability, the ability to make some part immutable is a virtue.

That's an orthogonal issue, and can be done in many other (and more general) ways.

> For two, in languages without explicit variable declaration, which variable is defined where quickly becomes murky when you have implicit capture. I have so many frustrations where the inner `i` variable clashes with the outer `i` in Python. Yes, I could just use a different name, but naming is hard, and with a new scope I should be able to reuse the name. That is almost the whole point of opening a new scope!

You're right: that is the point of opening a new scope! That sounds like a flaw in Python's design and could be remedied by making variable definition syntax different from assignment syntax. Consider Lua with its `local` syntax, C and kin with their type annotations, the Lisps with their completely separate forms for variable definition and assignment, and so on. There's also the Tcl strategy of "it's a definition unless it was imported into this scope with `global` or `upval`; otherwise it's an assignment".

> For three, in Javascript where closures are everywhere due to the amount of callbacks, the reference graph is just impossible to analyze. A closure may closes over another closure which closes over an object with a reference to the original closure. An explicit capture list makes the programmer think, and ease the job of anyone who tries to spot memory leaks from the source code. (But I guess that is just not the Javascript style, as they are so fond of never letting the programmers know about their mistakes. At least in C++ we trade that for speed. I don't know what Javascript trades that for.)

JavaScript is a shitty language to begin with, and fixing it wouldn't be as simple as fixing C# or Python... You make a good point here, but I still think that better tooling for data-flow analysis is a more attractive choice than a compulsory explicit capture list. On the flip side, an optional capture list could be a good compromise.

Post reply on HN