10 ms·
An empirical study on the impact of C++ lambdas and programmer experience
- byuu 10y agoContext always matters. I use lambdas sparingly in my applications, except for one major area: user interfaces. I can't begin to stress what a huge timesaver it is being able to bind a button's callback to a quick lambda instead of having to bind a callback to an std::function, add the function to the class header, and then put the actual button-click code somewhere else in the project in a separate function ... and then repeat that process for a large UI with hundreds of widgets. It's not even the initial extra typing, it's having all the code right where it's most relevant instead of scattered around everywhere and having to name all of these callback functions.
- deleted 10y ago[deleted]
- spacelizard 10y agoYeah I think this is the real win - the examples in the paper don't really cover any of the real reasons why someone would use functional programming. The places that I've used C++ lambdas a lot are callback-heavy code, i.e. things with a lot of asynchronous I/O, multi-threading, etc. While I don't doubt the validity of the argument that it takes longer for a programmer to write correct lambda code in C++ (I have been using them since C++11 was released and still forget the capture list syntax sometime), it's not much more than an academic exercise to take some iterator-based code and replace it with higher-order function calls. It's unfortunate that all the tasks seem to be based around doing that though. I do think it is still reasonable to do this in C++11 once you know how - with -O1 turned on you can get basically the same code spit out using std::transform, std::accumulate, std::for_each, et al, as you would using a for loop. It's also unfortunate to note that, at least in g++ and clang, there is still significant advantage to using lambdas over std::function and std::bind. Lambdas typically end up being faster in both compile-time and run-time, and end up generating less code. For these reasons I've found myself using them a lot more, especially anywhere I would have done some type of currying. This I do miss somewhat, but it's still leaps and bounds ahead of something I would have written in C++03.
- byuu 10y ago> It's also unfortunate to note that, at least in g++ and clang, there is still significant advantage to using lambdas over std::function and std::bind. I have to say, std::bind is just a travesty. It's so difficult to bind a member function pointer that takes multiple arguments to an std::function. I wrote my own so that you can do this with just: function<void (int, int)> f = {&Class::func, &object}; Source is here: http://hastebin.com/raw/kobudabasa http://hastebin.com/raw/kobudabasa In doing so, it becomes clear there's basically two ways to implement this idea: 1. you allocate heap space to perform type erasure. This results in a pointer indirection plus a virtual function call worth of added overhead. Along with tremendous costs to allocate and destroy the function objects. 2. you store a big chunk of raw memory inside the function<> class, and cast it as necessary to a pointer. This is actually what I did prior to C++11 and lambdas. It was tricky because the size of member function pointers is undefined. Having a vtable makes them larger. So for that I made a complex dummy class to pull its sizeof info. Option 2 is definitely a good bit faster (at least for constructing/copying/reassigning/destructing them), but you're really butting up against undefined behavior and abusing the language. And capturing lambdas would be even more challenging. But even with that, yeah, you can't ever really beat concepts that are native to the language like lambdas and virtual functions themselves. Compilers can get really clever and inline things in a way that's exceedingly unlikely to ever occur with std::function, no matter how you implement it.
- ambrop7 10y agoI've also written my own "version" of function but intentionally lightweight, for an embedded project: https://github.com/ambrop72/aprinter/blob/master/aprinter/base/Callback.h https://github.com/ambrop72/aprinter/blob/master/aprinter/ba... Callback<void(int, int)> f = APRINTER_CB_OBJFUNC(&ThisClass::function, this); One magic thing is that the macro is just a value, it figures out the type itself. There's zero memory allocation. The Callback class just contains a function pointer and a void pointer. So you can't bind argument values other than the object pointer, but I have no need for that.
- porges 10y ago> After instructions, participants were given printouts of sample code they could refer to while solving tasks. Group Lambda got code of a C++ program using lambda expressions and group Iterator received code of the same program written using iterators. They then had time to study the samples before starting the tasks and could refer to these samples later. These samples do not appear in the paper, so we don't know what they saw. The “iterators” discussed are Java-/C#-style iterators, not C++ ones (as I expected reading the abstract). In a C++ context I would have expected lambdas vs iterators to be something like: // lambda float retVal = 0; std::for_each(mb.cbegin(), mb.cend(), [&](item x) { retVal += item.price; }); return retVal; // pure iterator float retVal = 0; for (auto it = mb.cbegin(); it != mb.cend(); ++it) { retVal += it->price; } return retVal; ... and the first would be better off as: return std::accumulate(mb.cbegin(), mb.cend(), 0f, [](float acc, item x) { return acc + x.price; }); I think the need to use ref-capture (since you only get a side-effecting `std::function` to play with in their sample) would be the thing most likely to throw people off – as it’s something that should be avoided in most code, anyway ;)
- MaulingMonkey 10y agoYeah - from what I can see, neither interface looks like idiomatic C++. EDIT: Looks like you beat me to the punch on some of these ;) Instead of: float getSum(marketBasket mb) { float retVal = 0; // Implement solution here // --------- marketBasket::iterator iter = mb.begin(); while (iter.hasNext()) { retVal += iter.get().price; iter.next(); } // --------- return retVal; } I'd rather see real SC++L compatible iterators (as hopefully taught) and saner naming: float getSum(marketBasket market) { float sum = 0; // Implement solution here // --------- for (marketBasket::iterator item = market.begin(); item != market.end(); ++item) { sum += item->price; } // --------- return sum; } And instead of being pre-provided with a function <void(item)>, if I'm reading the pdf correctly: float getSum(marketBasket mb) { float retVal = 0; // Implement solution here // --------- function <void(item)> func = [&](item theItem) { retVal += theItem.price; }; mb.iterateOverItems(func); // --------- return retVal; } I'd rather see: float getSum(marketBasket market) { float sum = 0; // Implement solution here // --------- market.for_each([&](item theItem) { sum += theItem.price; }); // --------- return sum; } Or venturing into the far more functional style, where lambdas start to shine for me, personally: float getSum(std::vector<item> market) { // Implement solution here // --------- return std::accumulate(market.begin(), market.end(), 0.0f, [&](float sum, item theItem) { return sum + theItem.price; }); // --------- } It looks a bit better in C# where your selection of standard functions is a little less anemic and a little nicer to use: float GetSum(MarketBasket market) { // Implement solution here // --------- return market.Sum(item => item.Price); // --------- }
- EGreg 10y agoI used to really like C++ ... back in 2000. That is before they added the kitchen sink into the language. I prefer my computer languages to be like Chess - a few rules but efficient and expressive. Like C. Why? Because everyone can read the code others on the team wrote, without being a language lawyer and knowing tons of esoteric features and magic. That has implications for maintainability and team productivity, and the bottom line for a business. Really, people, having basic coding style conventions instead of tons of language features wasn't so bad: int Foo_bar(struct Foo* this) { // even this is readable }
- lambda 10y agoVery few people can read and understand C without being a language lawyer an knowing tons of esoteric magic. There are many things in C which look perfectly reasonable but which actually result in undefined behavior, and you cannot reason about what a program will do in the presence of undefined behavior.
- EGreg 10y agoBut there is a difference between using C in crazy ways, and using 20 features of a language in ways that make them interact. The more features a language has, the more I have to know just to read someone's code or be productive in a company - and the more chance someone doesn't know about potential harmful interactions and side effects.
- eropple 10y ago> there is a difference between using C in crazy ways As per my sibling, write me something as straightforward as a C++ RAII-using dtor in C without "using C in crazy ways". I will not hold my breath. The cost of a very minimal subset of C++ is literally zero, and yet significantly improves the likelihood of your code actually working. You're picking social baggage in either case: either understanding the subset of C++ that your team is using or expecting all of your developers to be perfect where C++ (and other languages--shouts, Rust!) just do it for you, correctly. Technology is socially interesting in that incompleteness and inexpressivity is so often misread as "elegance" or "minimalism". That a language is "simple" is not a feature when it offloads all of the danger onto the (almost invariably failing to consider critical information at the worst possible time, and I include myself at the forefront of that characterization!) developer. This is why we have tools that compensate for the most common, and most destructive, of our mistakes. (This post should not be construed as any particular endorsement of C++. C++ is a gong show when used improperly. But at least it's possible for a mere mortal to use it properly.)
- gohrt 10y agoSubmitter's editorialized title mischaracterizes the OP > All the tasks focused on iteration on a collection using a C++ vector object. That's comparing "functional style" collections to iteration. It is readily apparent that C++ syntax is so troublesome that "functional collections" won't be a win for small iteration blocks. We had the same debate for functional Java collection methods (although the Java 8 lambdas tilt it more closely in functional Java's favor) The main use case for C++ lambdas is for declaring callbacks.
- emcq 10y agoThe paper seems to mainly compare iterators vs lambdas. This seems like a bit of a strawman; the best use of lambdas is beyond iterators. For example, consider callback heavy asynchronous code. A promise library with lambdas is much easier to write and read than the equivalent state machine. I would go as far to say any mechanism where function chaining is useful, such as the nice data to mark/SVG abstraction used in D3, and also in promise libraries, has advantages with lambdas. Not only do you avoid having to write extra classes or methods, but the code is more succinctly logically grouped together, requiring fewer indirections to get to the transformations occurring.
- alfalfasprout 10y agoBasically anywhere where you would have used a callback in C code could probably benefit from a C++ lambda. It's easier to see what's going on, you don't litter your code with hundreds (or thousands) of tiny functions, and the compiler can easily inline everything. The fact that you can capture whatever you need makes it super easy to use inside a class if needed (eg; you need to access class members). It seems really dumb to me to declare that lambdas are detrimental to novices when it's clear they have a great deal of utility outside of something like replacing iterators.
- eksemplar 10y agoI'd much rather have a lot of smaller functions with single responsibilities, but then being middle management I worry about things I didn't when developing. I need the code to be SOLID, I need the time to market to be as small as possible and I need to be able to replace any developer with any developer on a moments notice. When students don't know lambdas you're costing me money by using them, because you made the training process longer.
- silasdavis 10y agoI'm glad I don't work for you...
- 10y ago
- nice_byte 10y agoI'm a professional and you can take the lambdas out of my cold, dead hands. Also, their examples are removed from reality. I've never seen people use lambdas like that. Most of the use cases I've seen are callbacks that get triggered on certain events (i.e. "display notification when background loading thread completes") and predicates (i.e. find_if). I see neither in their examples.
- jonstewart 10y agoOf course students had problems—-it's one more damn thing for them to learn about C++, likely from professors that don't know it very well either. But <algorithm> is way more useful these days, and boost::asio is a dream.
- pjmlp 10y agoUnfortunately most teachers aren't like Kate Gregory, and I fully agree with her, they should stop teaching C -> C++. Already in 1994 it was obvious to me that it leads to bad unsafe code and worse, the mentality to micro-benchmark each code line.
- alfalfasprout 10y agoThere's pitfalls to everything. I find that a lot of people that only learned C++ end up writing nice-looking safe code that's very well organized but has awful memory usage and dubious performance characteristics. It's not that they're bad programmers or made poor algorithmic choices. It's that they've been indoctrinated with the fear of "premature optimization". Moreover, they don't really know any better if they do have optimization opportunities. On the flip-side, the C programmer who starts out learning C++ will often spit out some hideous abomination that uses no namespaces, consists of obscure function names, pointers everywhere, and tons of callbacks. Thing is, sometimes performance really matters. A large part of the HN audience focuses only on web-oriented programming. But in scientific computing, finance, etc. being able to squeeze out a few more operations/CPU cycle can be incredibly important. I think rather than being dogmatic about the issue, as programmers tend to do, it's important to introduce students to C but be very clear about why you might want to use some of its features vs. relying on the safer C++ variants.
- GeneralMayhem 10y agoThis is ridiculous. C++ lambdas (and std::function) don't replace iterators except for the most fervent disciples of the Church of Haskell. They replace single-function interfaces in cases where you would have had to put together a custom struct that did exactly the same thing as a lambda with capture but in about 15 more lines.
- noobermin 10y agoTo be fair, the most fervent disciples of the Church of Haskell would use Haskell I'd think :) I only scanned the paper, but I get the impression the title was chosen specifically to grab attention. "An empirical study comparing the use of C++ lambdas versus C++ STL iterators and programmer experience" wouldn't get eyes on their presentation, and eventually, being posted to HN...well, it would, but they'll surely get more reads/downloads this way. Clickbaiting for conference proceedings, who would have known.
- jytqtrrrrr 10y agoSuch a title wouldn't be right, either. Their poor iterators don't seem to act like real iterators.
- posterboy 10y agoDon't you mean the church of A. Church? ;)
- lisardo 10y agoAsk them https://www.facebook.com/haskell.first.united.methodist.church/ https://www.facebook.com/haskell.first.united.methodist.chur...
- 72deluxe 10y agoYes, especially irritating in C++2003 here at work! you realise how useful the capture list is with lambdas when you have an in-function struct that can't see outside its own scope so you need to pass everything in (and won't have access to private portions of the outer class). Lambdas really are great.
- ryporter 10y agoIt's important to note that the negative impact documented in this study is in development time and whether the task is completed. It makes no claims about impacts on maintainability that show up later in the development process. It also does not measure the longer term impact on development time if a team starts using lambdas and gains experience over time. I say this not to dismiss the study, which appears to be fairly well done and to provide interesting results. I'm simply saying that its results are not inconsistent with lambdas providing a net, long-term benefit if introduced to a development team at work.
- NotThe1Pct 10y agoI have used lambdas heavily in the past and then stopped. Getting away from the rabbit hole that are lambdas + SFINAE + variadics increases productivity immensely.
- ThePhysicist 10y agoRecently I wrote a parser generator, which would take a rule structure and return a function that does the parsing. Lambdas were very useful here, and I do not know how I would've implemented this without them, at least in an efficient manner, because they let me do something like this: Parser ParserGenerator::compileLiteral(Rule& rule){ const string literal = rule.value; Parser parser = [literal] (shared_ptr<State> state){ //parse the literal... state.Advance(literal.size()) return state; } return parser } Solving this without lambdas would require a different encapsulation mechanism for the rule data, such as a class, which (IMHO) would make the code less idiomatic. So lambdas are actually very useful!
- nly 10y agoWell, an alternative approach might to not to couple parsing and state manipulation :) PEGTL does this. Approximately, you have a template parameter MyAction<> on the Parser<> template which in turn calls "MyAction<Rule>::apply (state)" when parsing of each rule is complete. It's a fantastic library. I highly recommend giving it a whirl. [0] https://github.com/ColinH/PEGTL/blob/master/doc/Actions-and-States.md https://github.com/ColinH/PEGTL/blob/master/doc/Actions-and-...
- partycoder 10y agoSome people feel comfortable expressing things in a more traditional way, in part, because you cannot change the habits built over the course of decades by just announcing a new standard. After the standard including lambdas came out, compilers did not immediately comply to it, and it took some time for them to catch up. Then it took even more time for tutorials and books to catch up. And it will take time for the C++ community as a whole to catch up as well.
- gpderetta 10y agoCompilers (at least clang, MSVC and GCC) released versions supporting lambdas well before the C++11 standard was finalized.
- partycoder 10y agoI see. The feature parity wasn't 100% I remember. There was also Boost Lambda since at least 2004
- flohofwoe 10y agoTitle of the research paper should be "C++ is not Haskell", but snark aside, I find C++11 lambdas useful to replace old-school callback code (of course only if callbacks make sense in a situation) because: - if local variables need to be captured, the code is much less noisy than using the old std::bind mess - non-capturing lambdas also work for C-style function pointers, and in this case the generated code is very efficient (no std::function object created under the hood) If you're aware what happens under the hood (...that in most cases a std::function object will be created, which in turn might do a dynamic memory allocation to hold captured variables), lambdas are a useful syntactic sugar and one of the more useful additions in C++11 (IMHO).
- ahmedtd 10y agoI'm pretty sure that most uses of lambda don't involve creation of std::function (and thus dynamic allocation and virtual function calls). As long as you maintain the distinct type of the lambda, any captures (by reference or by value) will just be fields in a stack object.
- bjourne 10y agoCan you always replace lambdas with iterators? For example, to visit a graph you could implement it using function that takes two arguments; the graph to visit and a lambda holding the function (functor) to apply to each node. It is fairly easy to implement and the compiler will inline the functor so there is no performance penalty. Any solution based on iterators that I can think of would be much more complicated and would probably not lead to as efficient code. I'll admit that the "user interface" for iterators are much nicer than using functors. The former meshes well with c++:s for loop syntax while the syntax for lambda constructs is bad.
- kentonv 10y agoAs someone who writes a lot of asynchronous code in C++, lambdas have changed my life. The paper seems to study the use of lambdas specifically as an alternative to iterators. I actually mostly still use iterators myself, rather than lambda-based alternatives, so I agree: they don't have a huge impact there. But the headline is misleading. It should have had "when compared with iterators" added to the end.
- Fede_V 10y agoLambdas are an incredibly useful construct, and, once you master them, they do make a lot of programming tasks much easier. However, C++ lambdas picked the most horrifically ugly syntax possible, and they switch between three subtly different semantics (copy, reference, move) depending on a single glyph. I feel bad for the people working on modern C++ - maintaining backwards compatibility is a huge constraint upon design space. I'm not surprised that lambdas slow down people that aren't already experienced with them. Since I rarely program in C++, whenever I go back to it, I always have to spend a bit of time bashing my head against the horrific syntax.
- lorenzhs 10y agoC++14 improves upon lambda captures a lot. I'm also not aware of a move capture, only "by reference" (&) and "copy" (=). In C++14 you're allowed to put arbitrary assignments into the capture, e.g. [foo=std::move(bar)]() {}
- beached_whale 10y agoI did that in the C++11 days by creating a bastard class template that would subvert the copy operator and turn it into a move and capture that by value. It was ugly as heck and was removed as soon as I have c++14
- TillE 10y agoSerious question: how would you improve the syntax while maintaining their feature set (which is not huge, but includes a few important things)? I understand that C++ lambda syntax can be confusing on first encounter, but after reading about them for five minutes it should be very naturally comprehensible to any experienced C++ programmer. It's terse, but very clear.
- arunmu 10y agoHow and who allowed them to publish such a ridiculous paper !!?? There is so many un-idiomatic C++ code... And does the topic even fit to be published as part of ACM ??
- delegate 10y agoThe only true conclusion of this study is that students find C++ a bit intimidating. Which is not surprising at all, because C++ is a very complicated language ! It takes years, even decades before you can master this language - even then you probably won't be using all of its features. Back in the day, it took me a long time before I could truly grasp the C strings and pointers in general. Yes, I could write code with pointers and C strings (new char[length + 1] and delete[] str; were my buddies!), but sometimes I was programming with my eyes closed and fingers crossed, because the implications of sharing raw pointers were too complex, especially if threads were involved. Same with these new features. They may 'look' more modern due to the updated syntax, but the underlying concepts that they are hiding still need to be well understood. So not surprising that students struggle with getting it right the first time. The good news is that with a bit of experience this becomes easier, as with anything else..persistence is key.
- 72deluxe 10y agoYes C++ is complicated and yes students struggle with programming. It takes years to understand how to put a decent program together (separation of data, logic and transport etc). As for your guesswork with C-style strings, you should have been using std::string and using copy constructors to pass work into a thread (eg. who owns the data the thread is working on?). With modern things you can move the data into the thread instead of copying it. I would recommend students working on their own personal projects as a good pasttime as it further helps anyone understand how to write a decent program and learn the language. Also, tell them to read Stroustrup's book.
- delegate 10y agoI started programming back in the day when every programmer wrote his own String class :). Standard library was, for one reason or another, unpopular back then. Or maybe because of the C Windows API and later MFC, which came with its own CString class...
- 72deluxe 10y agoAdd to the confusion CString, BSTR and std::string and std::wstring. What could go wrong?
- ericmo 10y agoPaper says that "those in lambda group received no benefits in regard to [...] compiler errors". Really, would anyone expect lambdas to improve compiler errors? Also, isn't that a problem of the compiler, and not of the lambda idiom in C++ or C++ language itself? I mean, idk about clang, but at least compiler errors from msvc and gcc have always been hard to understand, with or without lambdas.
- Ericson2314 10y agoI'm so relieved the consensus is negative here.
- JohnLeTigre 10y agoThere's a guy at work whose position is unclear to most. It's just that random guy. Typically, on monday mornings and friday evenings, he's very loud and looking busy. This is an age-old trick to superficially keep your job. Ever so often one would hear his odd sounding voice pierce through the office talking about some idiosyncratic evidence while looking proud of himself. Truly, if this guy was a programmer (which I doubt), he would be using lambdas. See, lambdas might have a use after all...
- eco 10y agoAs a working C++ programmer for over a decade this conclusion is absolute insanity to me. People (myself included) are actually using the STL algorithms now because you no longer have to go through the pain of writing a functor. Every time I can avoid writing a for loop I'm a little happier (and my code is a lot more readable). When lambdas first came about there was a lot of "meh, I could already write a functor just fine" but the ease of using them is just so much nicer and the readability of the code is so much greater. Just writing a plain for loop would often be less code than using an STL algorithm and a custom functor so there was very little incentive to use the highly tuned STL algorithms if it was just going to take more boilerplate. Now there is and we are finally seeing C++ programmers catching up to the insights that functional programmers had made a long time ago[1] Others have said this but comparing lambdas to iterators is a very odd decision because they are completely unrelated things. The conclusion should really be that anonymous functions aren't as intuitive as "regular" imperative code. The regular code walks you through what is going on in babysteps so even a new user can figure out what is going on. The way with lambdas requires a bit more knowledge but for working programmers who aren't learning C++ for the first time they are obviously a big boon. Sometimes you have to learn something slightly more complex to reap the benefits of it. Pointers are very hard for brand new programmers to wrap their heads around but nobody would argue that judicious use of them can be a real benefit (or essential, even) in some situations. This is like concluding that smart pointers are bad because they are less explicit about what is going on. You still need to learn C++ manual memory management but your code will clearly benefit (both in terms of verbosity and memory safety) by using smart pointers 95% of the time. 1. CppCon 2016: Ben Deane "std::accumulate: Exploring an Algorithmic Empire" https://www.youtube.com/watch?v=B6twozNPUoA https://www.youtube.com/watch?v=B6twozNPUoA (std::accumulate, fold essentially, has been in C++'s STL since the beginning and Stepanov probably had it in mind before C++ even existed but we are just now getting lectures like this and I think lambdas are to thank for this)
- lcvella 10y agoIt seems that the most important conclusion of the article is that using lambdas is more error prone than using iterators. What begs the question: how many compiler errors were caused by the actual lambadas, and how many were caused by the declaration of a `std::function` variable, as induced by the authors (that besides unnecessary, is somewhat of a bad style)?
- alexeiz 10y agoIs the paper available in public domain? If not, I won't regret much. Looks like it's badly written and filled with un-idiomatic C++ code.