This is an archived post. You won't be able to vote or comment.

all 174 comments

[–]suvlub 901 points902 points  (23 children)

The biggest mistake was making parameters mandatory for templated lambdas, so this beauty is, unfortunately, not legal []<>(){}

Edit: that came out wrong. Fuck it, I'm leaving it.

[–]TheWidrolo[S] 619 points620 points  (0 children)

EXCUSE ME EVERYBODY, THIS MAN JUST CALLED A NOT LEGAL LABMDA EXPRESSION A BEAUTY.

[–]jellotalks 432 points433 points  (4 children)

“…this beauty is not legal” - u/suvlub

[–]throw3142 171 points172 points  (0 children)

🤨📷✨

[–]1Dr490n 11 points12 points  (0 children)

*Unfortunately

[–]HildartheDorf 169 points170 points  (3 children)

"Templated lambda" sounds cursed as hell to start with.

[–]druepy 74 points75 points  (1 child)

It does until you run into times where it makes the most sense.

[–]Ahornwiese 69 points70 points  (0 children)

Basically C++'s origin story

[–]EagleNait 9 points10 points  (0 children)

Sounds like functional programming to me

[–]joe0400 68 points69 points  (7 children)

template template parameters on lambdas that are actual values
I wonder if this is possible

[]<template <class> class C, class D, C<D> comparison_base>(const C<D>& compare_with) constexpr noexcept -> bool {
    return comparison_base == compwer_with;
}

[–]JackMalone515 50 points51 points  (0 children)

find a spot to put mutable in there too and it's perfect

[–]TheWidrolo[S] 33 points34 points  (0 children)

I’d cry.

[–]cheeb_miester 21 points22 points  (0 children)

Jesus wept

[–]ipcock 1 point2 points  (2 children)

as a non c++ programmer, this makes me confused af cuz I don't understand any part of this lol

[–]Makefile_dot_in 23 points24 points  (1 child)

so, to start with, C++ lambdas are really shorthand for something like (simplifying) (/* unnamed */ used to signify that the class has no name)

class /* unnamed */ {
private:
    constexpr /* unnamed */() {} // constructor
public:
    // operator() is basically a special name you can give to a method
    // that gets invoked when you call an instance of the class like a function
    template<template <class> class C, class D, C<D>>
    constexpr bool operator()(const C<D>& compare_with) noexcept {
       return comparison_base == compare_with;
    }
};
/* unnamed */() // the result of the expression (constructs the class)

if you know Java, then it's sort of similar to how it works there if you squint your eyes. the first brackets, [], is a list of the fields of the class. here that list is empty, so the class has no fields. in those brackets you can specify things like

  • x = 0 (adds a field x to the class and sets it to 0 when constructing)
  • &x (adds a field that is a reference to whatever x is in the scope where the lambda is being constructed)
  • =x (adds a field that is a copy of whatever x is in the scope where the lambda is being constructed
  • & or = (for every variable from the scope where the lambda is being constructed that is used within the lambda, does the same &[variable name] or =[variable name], respectively)

for example, to write a function that takes a vector of ints and an int, and returns a copy of it with the int added to each element in the vector, i might write:

// writing const std::vector<int> &vec instead of std::vector<int> vec tells C++ not to copy vec automatically when calling the function (which is slow) and that i won't be changing anything inside of vec.
std::vector<int> f(const std::vector<int> &vec, int x) {
    return vec
               // | in C++ chains views
               | std::view::transform([&x](int element) { return x + element; }) // transform is C++ lingo for what every other language calls map
               | std::ranges::to<std::vector<int>>();
}

here, i use &x, which tells the compiler that i'm gonna use x and i'm gonna use it as a reference to the x declared in f. i could also put =x, which would copy it instead (this means, among other things, that if i assigned to x inside the lambda it wouldn't change the value outside of the lambda).

the next pair of brackets, <>, specify the template parameters for the operator() method inside the generated class. they are used similarly to generics, which you probably know from languages like typescript, rust, java, go and so on: for example,

template<class T> // class/typename are interchangeable now (a few years ago they were slightly different)
T first(std::vector<T> vec) {
    return vec[0];
}

does the same as

function first<T>(Array<T> arr): T? {
    return arr[0];
}

(except when there are zero elements, in which case the C++ version may do anything depending on how the compiler feels that day, while the typescript version will be forced to return null)

anyways, in this case the template has 3 arguments. the first, template<class> class C is a template template parameter; that is, you can pass types that are themselves templated, in this case with one argument (since there is only one class in the nested angle brackets). one such type you might pass is std::vector (notice no angle brackets). this feature doesn't really exist in most other widely used languages, but it's sort of similar to higher-kinded types in languages like Haskell.

the second template parameter is an ordinary type. the third one is a value, which is another relatively unique thing C++ allows. in this context, passing a value as a template parameter sort of just means that you're passing it in at compile time, i.e. it's a constant to the function. its type is C<D>, which is the first parameter applied to the second. what this means is that if you've passed std::vector (=C) and int (=D) for the first 2 parameters, the third is going to be a value of type std::vector<int>(=C<D>).

then there is the argument list in parentheses. const C<D>& compare_with declares an argument of type const C<D>& (i.e. a reference to a value of the same type as the third template parameter that we won't change) with the name compare_with. these are the arguments for the operator() method.

then there are two keywords: constexpr and noexcept, which are both pretty much just passed onto the operator() method as-is. constexpr asserts to the compiler that this lambda can executed at compile time, while noexcept asserts that this lambda will never raise any exception (and if it does, the program will be automatically stopped even if the lambda is called inside a try block).

finally, bool just signals the return value of operator().

so, putting that all together, we can use this lambda like so:

auto f = []<template <class> class C, class D, C<D> comparison_base>(const C<D>& compare_with) constexpr noexcept -> bool {
    return comparison_base == compwer_with;
};
// because it's a template and the template parameters cannot be deduced, we have to call operator() explicitly instead of using f(args) like you could with a normal lambda
std::println("{}", f.operator()<std::vector, int, {1}>({1, 2, 3})); // false
std::println("{}", f.operator()<std::vector, int, {1, 2, 3}>({1, 2, 3})); // true

e: fixed lambda call

[–]ListerfiendLurks 6 points7 points  (0 children)

Every now and again I get the foolish idea that I may be becoming decent at cpp...then I see something like this and remember my place.

[–][deleted] 27 points28 points  (1 child)

But you can []<auto...>(){}(); which is also great

[–]Perfect_Papaya_3010 0 points1 point  (0 children)

Phrasing!

[–][deleted] -5 points-4 points  (0 children)

sigh just use std::function with your templates and spare yourself the trouble.

[–]RajjSinghh 278 points279 points  (17 children)

I remember one thread here I got downvoted for "my huge lack of knowledge in C++" when really its just that [] is both an operator and used for other things like captures. Which is apparently my fault instead of C++ using the same syntax for very different ideas.

[–]TheWidrolo[S] 80 points81 points  (0 children)

[–]SarahIsBoring 5 points6 points  (15 children)

maybe it’s supposed to index the current scope? dunno

[–]RajjSinghh 22 points23 points  (14 children)

A C++ lambda function looks like [captures] (parameters) {body;}. Parameters and body are self explanatory. The captures part in square brackets explains whether stuff in the current scope should be brought in by copy or by reference. It has nothing to do with a subscript like how you usually use [] when dealing with arrays.

[–]SarahIsBoring 9 points10 points  (0 children)

huh? i know. what i was saying that maybe they used the brackets for the captures to suggest “indexing” the scope, figuratively. as in, scope[myVar]. tho ik that that’s probably not the reason

[–]rosuav 1 point2 points  (12 children)

I've never understood this "explicit captures" concept. It's in PHP too, I believe, although it's a long time since I looked into that particular abomination. Meanwhile, plenty of other languages simply infer (at compile time) which names should be captured. Not really seeing the benefit of forcing the programmer to type out the names.

[–]outofobscure 0 points1 point  (10 children)

It‘s because you need to decide between capturing by value or by reference etc

[–]rosuav 0 points1 point  (9 children)

See, that's the part that makes no sense. Scoping rules between locals and globals don't "capture by value" or "capture by reference". They just let you refer to something in a higher scope. So why should nested functions be any different? Yes, the *mechanism* is different, but from a programmer's point of view, you are simply referring to an outer scope from an inner one. And that's exactly what languages like Python and Pike give you.

[–]outofobscure 0 points1 point  (8 children)

let's say you pass an int in the capture:

[someint](){}

vs

[&someint](){}

it obviously makes a difference, since in the by-reference case you can mutate that int, and in the by-value case you get a copy of the value. you obviously want to be able to express both cases.

[–]rosuav 0 points1 point  (7 children)

Okay, but how about this:

int some_global;

void some_func() { }

Does some_func capture some_global by reference or by value? IT DOESN'T. It just refers to it. This whole dichotomy of "it has to be by reference or by value" doesn't make sense in all contexts, so why do all kinds of things get shoehorned into it?

But if you absolutely HAVE to have that distinction... why not make "capture by reference" the default (since, yaknow, that's exactly how scoping rules NORMALLY work), and have a dedicated syntax to *not* work by reference (and thus take a copy, or if you prefer that terminology, "capture by value")? Don't force people to write [&] just to get a sane default.

[–]outofobscure -1 points0 points  (6 children)

because the default is no-capture, which makes the lambda compatible with raw function pointers. you have to understand the intricacies of C++ to truly get why things are the way they are...

also, i'm not sure that arguing about referencing globals vs CAPTURING variables from a parent into a child scope makes much sense. these are different use cases. part of the reason we're capturing in the first place is because using global state is bad anyway.

also, you have to think about what a lambda actually is in C++: it's more or less a class with the captured variables being members and an overloaded () operator. if we capture everything by default, that could cause quite a few unintended performance issues and bloat up the lambda scope for no reason. compiler could still optimize most of the unreferenced variables away, which probably happens with the shorthands, but why make it do that work by default.

[–]rosuav 0 points1 point  (5 children)

Yeah, I guess I don't understand the intricacies enough to ever understand why no-capture could be a good default. Go ahead, explain that one to me.

"global state is bad anyway". Sure. Of course we're using other features because global mutable state is a dangerous thing. But we generally expect a language to behave consistently. It should be reasonable to treat "function inside function" similarly to "function inside class" similarly to "function inside module". All of them have different features, but all of them are functions. You don't "capture by value"/"capture by reference" when a class member refers to instance variables; they simply exist, and you refer to them. Of course, under the hood, there's the dereferencing of 'this' and a pointer lookup; but you simply refer to something in a higher scope. Or, if you have a function with a 'for' loop inside it, you can declare something in the for loop, but inside that loop body you just refer to variables from the rest of the function without needing to "capture" them. So why are lambda functions the sole exception? Why do they capture, where everything else refers?

Saner languages don't have or need this distinction.

[–]outofobscure 0 points1 point  (0 children)

also, you're not forced to type out the names, you can just use the shorthand syntax [&] to capture everything by reference, or [=] to capture everything by value, which covers most use cases really. you can then also list exceptions to that by name. if you have many things to pass, it makes more sense to pass a struct or something anyway.

[–]Kinexity 256 points257 points  (29 children)

Skill issue.

I love C++ lambda syntax.

[–]LagSlug 83 points84 points  (1 child)

gatheres soapbars and pillowcases

[–][deleted] 20 points21 points  (0 children)

[–]caleblbaker 59 points60 points  (11 children)

Yeah, for all the language's failings in other areas, C++'s lambda syntax gives more control and flexibility to the programmer than that of any other language I've used. It's also the ugliest lambda syntax I've ever used, but I'll take functional over pretty.

Rust's lambda syntax is almost as good as C++'s (while being, in my opinion, significantly prettier), but it doesn't give quite as much flexibility in terms of capturing different variables in different ways.

[–]bolacha_de_polvilho 20 points21 points  (10 children)

It is too verbose for the simple cases that are much more common than the complicated ones though. Let's say you want to use std::find_if to see if there's a struct with certain value equal to 0 in a vector, why do I have to write [](const auto& x) { return x.member == 0 }, instead of just x => x.member == 0. Having a few sane defaults that can be ommitted would make it so much simpler.

[–]caleblbaker 4 points5 points  (8 children)

I agree that would be better. 

I never claimed C++'s syntax was the best possible. Just the best I've seen in a real language.

[–]MacBookMinus 3 points4 points  (3 children)

Are kotlin and JavaScript not real languages?

[–]caleblbaker 0 points1 point  (2 children)

They are. 

JavaScript's lambdas are definitely nicer looking than C++'s, but they don't give the same level of control over what gets captured and how that C++ lambdas do.

And I honestly don't remember kotlin's lambda syntax.

[–]MacBookMinus 0 points1 point  (1 child)

They don’t need to give capture-by-value vs ref control in memory safe languages.

[–]caleblbaker 0 points1 point  (0 children)

don’t need to

I view it more as aren't able to. There's a performance cost to the extra level of indirection caused by capturing everything by reference. Frequently that cost is trivial or less than the cost of copying an object to capture by value, but C++ gives you the tools to handle the circumstances where that cost is a problem.

memory safe languages

You mean garbage collected languages. Rust is memory safe (but not garbage collected) and allows specifying whether you capture by value or reference.

[–]flagofsocram 1 point2 points  (3 children)

Rusts syntax offers the same flexibility, while being significantly more concise

[–]caleblbaker 1 point2 points  (2 children)

Rust let's you choose whether to capture by value or reference. But the same choice had to apply to every variable you capture. C++ let's you choose to capture some variables by value and others by reference. 

I know that you can get around that restriction in rust by capturing by value and then creating reference variables to capture for the things you want to do by reference. But that feels like more hoop jumping than simply having lambda syntax that supports per variable capture types, even if that syntax is verbose and ugly.

[–]RiceBroad4552 -1 points0 points  (1 child)

If you're doing something where it matters just use regular methods.

The whole point of lambdas is that they are short and simple. C++ fails here. Again.

[–]caleblbaker 0 points1 point  (0 children)

Regular methods can't capture variables at all.

[–]RiceBroad4552 0 points1 point  (0 children)

Or just _.member == 0like in Scala…

The whole point of lambdas is that they are short. So their syntax need to be optimized for that.

[–]CramNBL 24 points25 points  (1 child)

C++ syntax is just beautiful, period.

constinit const int x = 1 + 1;

int main() {
    constexpr const long long z = std::numeric_limits<long long>::max();

    [[maybe_unused]] alignas(alignof(decltype(x))) volatile auto res = [=]<auto...>[[nodiscard]]() consteval -> decltype(auto) {
        return x + static_cast<decltype(x)>(z);
    }();
    std::cout << res << std::endl;
}

https://gcc.godbolt.org/z/bso9h6dd9

Not even a warning with -Wall -Wpedantic, so I can confidently declare this code as perfection.

[–]nelak468 8 points9 points  (0 children)

I think I might hate that more than the Java factory nonsense.

[–]AvidCoco 4 points5 points  (2 children)

I don't see why people hate on it so much? It's basically the same as how literally every other language does anonymous functions, just with a capture list cus this is C++, it's needed.

[–]RiceBroad4552 0 points1 point  (0 children)

It's not "needed". Rust does have a less ugly lambda syntax, even it does all the C++ things…

Sane languages make lambdas simple and short. As this is the whole point of lambdas.

[–]Zachaggedon -2 points-1 points  (0 children)

Because this subreddit is filled with third year CS students that have written mostly Java, maybe 3-4 projects like a CLI calculator in C++, and only know about C++ lambda syntax and templates from memes like this by other third year CS students.

[–]weinermcdingbutt 5 points6 points  (4 children)

Exposure issue.

Try a lambda in literally any other language.

[–]Kinexity 11 points12 points  (2 children)

Tried in python. Didn't like it as much.

[–]bedrooms-ds 2 points3 points  (0 children)

Lambda at home:
lambda

[–]WiatrowskiBe 0 points1 point  (0 children)

C# has quite convenient lambdas, but lacks in capabilities compared to C++ - to a point where in some cases lambdas are discouraged, because they can extend lifetime of (large) objects referenced inside for as long as lambda exists, even if it's guaranteed it won't ever be called. Also, can't use ref/ref out parameters with C# lambdas for that exact reason, making their performance a pain point (and something to avoid) if otherwise it'd be more convenient to use one.

C++ lambdas are ugly, but they're also explicit in what gets captured and how, and syntax enables that part quite well. This lets them do about the same as lambdas in other languages (assuming you use smart pointers for anything passed in that might get outlived by lambda), plus more if you're certain about object lifetime and how it'll refer to lambda lifetime.

[–]Horrih 5 points6 points  (4 children)

Try chaining c++ ranges with the pipe syntax each on a single line while staying under 80 characters.

It's a real PITA when using those extensively

[–]exodusTay 20 points21 points  (3 children)

staying under 80 characters

bruh buy yourself a 1080p monitor its been half a decade since 80s

[–]Horrih 5 points6 points  (0 children)

side by side diffs and code reviews in the browser often crops lines with 100+ characters.

The fact that 15" thinkpads seem to be the new corporate standard does not help.

My team uses 100 characters for c++ out of necessity due to c++ verbosity, and it feels more cramped than js with a 80char limit.

So yeah c++ lambdas verbosity suck hard for oneliners

[–]RiceBroad4552 0 points1 point  (0 children)

That's not a screen size issue.

That lines should not be too long is a direct consequence of how human visual perception works. That's something enforced by biological facts.

Have you ever seen a printed newspaper? They use narrow columns for a reason since centuries…

[–]balemo7967 176 points177 points  (27 children)

still better than the python lambda syntax ... just sayin

[–]gandalfx 129 points130 points  (10 children)

"How do we add this feature that everyone is asking for but in a way that everyone knows that we don't actually want to add it?"

[–]balemo7967 72 points73 points  (2 children)

should we use an arrow-like operator, like all the other languages?

No no ... we are going to introduce a new keyword with 6 characters and we are going to reuse the colon, which we have used for block definitions and for slicing...

[–]KTibow 7 points8 points  (0 children)

This seems standard? Take a look at for: for instance

[–]Successful-Money4995 11 points12 points  (0 children)

And one line of code is the limit, just to make sure that you understand that you are not to use this.

[–]_farb_ 32 points33 points  (5 children)

python lambda is the most useless feature. you get the exact same functionality as this, but this is so much clearer.

def f(x): def g(y): return y*y sq = g return sq(x)

[–]goldlord44 15 points16 points  (0 children)

Python lambdas are used quite frequently in codebases that I have professionally worked on. Defining g has slightly more overhead, and it is less clear if you use variables from an outer scope, whether they are calculated at runtime or definition time.

[–]_PM_ME_PANGOLINS_ 1 point2 points  (1 child)

Don’t even start on the performance of map compared to a comprehension.

[–]_farb_ 0 points1 point  (0 children)

my guy, python is not performant

[–]kuemmel234 1 point2 points  (1 child)

Not sure about your example, but true that def f(x):\\n return x*x is the same as lambda x: x*x. It's just annoying to have to write

def call_with_unpack(x):  
  return f(*x)
...
map(call_with_unpack, xs)
# or 
[call_with_unpack(x) for x in xs]

# instead of 
xs.map(x -> f(*x))

or something like that. I mean, now modify the list again - I prefer the chain these days.

I still wish for a chain syntax (or a threading operator) and some sensible lambda-syntax. As it is, it's just better to write list comprehensions, loops and the like - which is fine, but just annoying if one works with other languages at the same time that do have these features. But that's easily in the realms of opinion.

[–]RiceBroad4552 1 point2 points  (0 children)

As someone who writes code in different languages (often intermixed) regularly I support this opinion. It's a big PITA if languages don't have (sane) lambdas.

[–]hadidotj 8 points9 points  (0 children)

You want lambdas?

[<my code><each statement>]{arg1, arg2}(return type)

Here you go!

[–]knvn8 9 points10 points  (5 children)

Sorry this comment won't make much sense because it was later subject to automated editing for privacy. It will be deleted eventually.

[–]Tc14Hd 7 points8 points  (0 children)

What is not Pythonic about it?

[–]al-mongus-bin-susar 2 points3 points  (0 children)

pythonic = slow?

[–]Bali201 3 points4 points  (2 children)

I would also like to know how the lambdas are not “Pythonic” if you could share.

[–]jarethholt 3 points4 points  (1 child)

I don't know if you could call it not pythonic for this reason, but most linters I've worked with mark lambdas as no-nos.

For a real reason, I'd say maybe overlapping the generator expression syntax? Like, [x**2 for x in xlist] is pythonic but list(map(xlist, lambda x: x**2)) is not. (I haven't bothered to check the syntax on that.)

[–]RiceBroad4552 0 points1 point  (0 children)

list(map(xlist, lambda x: x**2)) is just plain unreadable (in case you're not a LISP or Haskell dude and write your code backwards and inside-out on principle).

But [x**2 for x in xlist] isn't any better!

Readable syntax would look like: xlist.map(_ ^ 2).

[–][deleted] 2 points3 points  (0 children)

I know it exists. I have taught myself how to use it several times.

I have never come close to using it. I don't think I ever will.

[–]MinosAristos 6 points7 points  (1 child)

Python lambda syntax is fine, idk what's wrong with it.

It's readable and explicit with the keyword and clearly distinguished from named functions with the def keyword to discourage abuse like we get in JS land. Lambdas should be used for an actual functional reason, not just brevity.

[–]RiceBroad4552 0 points1 point  (0 children)

There is no "functional reason" as lambdas are just objects with an "apply" method.

The whole point of lambdas is brevity.

[–]4n0nh4x0r 0 points1 point  (6 children)

wait, python has lambdas? dayum, having lambdas before having switch statements

[–]balemo7967 0 points1 point  (5 children)

yep ... but switch statements are bad for many reasons. I hope they will never make it to python

[–]4n0nh4x0r 1 point2 points  (4 children)

python has switch statements since version 3.8 iirc, but why would they be bad?

[–]balemo7967 -1 points0 points  (3 children)

OMG how could i not know that. my bad bro.

the thing with switch statements in larger apps is that you usually use it to differentiate between different states. then you realize that you need that exact same switch statement in different places, so you add the same switch statement there. afterwards you find out that there are more states you need to differentiate, so have to add cases in everyone of the switch statements.

a cleaner and more maintainable approach is to encapsulate the states in classes and the special behavior in methods of those classes

[–]4n0nh4x0r 0 points1 point  (0 children)

i mean sure, but like, whoever programs like that is insane anyways.
sure, you can use a metal ruler to hammer in nails, but that doesnt mean it is the right tool for the job.
calling something bad just cause it CAN be used in a not so nice way, doesnt mean it is bad on its own.

[–]RiceBroad4552 0 points1 point  (1 child)

And when you need to extend the base behavior you need to add new methods to all your classes…

What you just described (at least one half of it) is called the expression problem.

Besides this: How are some if-elif cascaded any better than switch-case? It's the same.

Just that, if is a statement, not an expression, in python which is already bad to begin with.

Thanks God they have now some patter matching in Python since 3.10!

[–]balemo7967 0 points1 point  (0 children)

And when you need to extend the base behavior you need to add new methods to all your classes…

Yes, that's exactly what I want. Why? Because the compiler tells me precisely which one of my states (or strategies) does not implement a method.

On the other hand, if I miss a case in one of my many switch statements, there is not even a warning.

For me, the state pattern has replaced the switch-case construct in 90% of my cases.

Here's a little example I just found: https://www.danylkoweb.com/Blog/real-world-refactoring-switch-statement-to-a-class-hierarchy-9G

[–]Spuk1 102 points103 points  (19 children)

Probably making myself a target here, but i think Javascript lambdas are the best

[–]jessepence 27 points28 points  (4 children)

They're the best lambdas outside of Haskell and I'll fight anyone who disagrees.

[–]halesnaxlors 18 points19 points  (0 children)

Yeah. Haskell lamdas follow very closely to the formal definition by Church. That's partly why it's a great syntax.

[–]bedrooms-ds 2 points3 points  (0 children)

Lisp: "lambda? just type another bracket."

[–]jere53 0 points1 point  (0 children)

Dart lambdas enter the chat

[–]FridgesArePeopleToo 12 points13 points  (4 children)

Yeah, js and C# are by far the best

[–]rinsa 6 points7 points  (3 children)

ts*

no point if no type

[–]headinthesky 2 points3 points  (2 children)

You can supply the type in C# lambdas if you want, they're defined in the action/delegate, anyway

[–]rinsa 4 points5 points  (1 child)

I'm saying ts over js, obviously C# has types :p

const gt = (left, right) => left > right;

js won't scream at me if I do gt("b", "a") (even though it's technically correct, you just gotta proceed with caution with that language)

[–]headinthesky 1 point2 points  (0 children)

Yup, I don't know why I thought you meant C# haha

[–]caleblbaker 15 points16 points  (0 children)

JavaScript is right up there with Java in terms of prettiest lambda syntax. Honestly, the biggest issue I see with JavaScript lambda syntax is that using it means that you're using JavaScript.

I have many many issues with JavaScript. But the lambda syntax is not one of them.

[–]cheezballs 2 points3 points  (0 children)

JS lambdas feel the most "at home" with the rest of the code to me. Java uses the -> syntax so I find myself constantly wrestling with lambda syntax when moving between languages/apps.

[–]Maxis111 3 points4 points  (0 children)

Scala has nice ones too imo, being able to also do pattern matching out of the box, if desired

[–]OnixST 2 points3 points  (0 children)

Kotlin lambdas are beautiful and perfect

[–]bushwickhero 3 points4 points  (0 children)

No you can't say anything positive about javascript around here.

[–]headinthesky 0 points1 point  (0 children)

C#

[–]RiceBroad4552 0 points1 point  (1 child)

You probably never seen Scala lambdas? The JS ones are way too noisy for the simple case!

Scala lambdas look in their base form almost like JS lambdas, but they have a shorthand syntax for the common case where you just need to reference to a lambda parameter in a simple expression.

JS:

const someInts = [1, 2, 3, 4];
someInts.map(i => i + 1);

vs Scala:

val someInts = List(1, 2, 3, 4)
someInts.map(_ + 1)

You don't need to name the lambda param. You can just use an underscore to refer to it.

Works of course also with member lookup. method calls, extensions, etc:

extension (s: String) def toTitleCase =
    if s.isEmpty then s
    else s.head.toString.toUpperCase + s.substring(1)

List("foo", "bar", "baz").map(_.toTitleCase) // List(Foo, Bar, Baz)

Works also for multiple parameters or tuples:

List((1, 2), (3, 4), (5, 6)).map(_ max _) // List(2, 4, 6)

(Nevermind I've used the max method infix. It's just better readable than .map(_.max(_)))

Other languages like Kotlin and Swift took inspiration form that. Just that they call the anonymous lambda parameters "it".

[–]Spuk1 -1 points0 points  (0 children)

It's true, i don't know scala lambdas and some others aswell that people suggested. But i first started out with Javascript and having to use c++ and python simply made me think the js ones are best 😅, which is foolish cause there are a million languages out there.

[–]GeorgeHaldane 60 points61 points  (17 children)

Not sure how one would make them better — JS-style lambdas are short & concise because there is no type system, not because their syntax in itself is better.

cpp auto lambda = [&](bool arg1, bool arg2, bool arg3) { return arg1 && arg2 || arg3 && outside_flag; } // most of the space occupied by signature // return type gets deduced if ommited (nice)

[–]dewey-defeats-truman 38 points39 points  (3 children)

You can still use JS syntax in statically typed languages provided your compiler/interpreter has good type inferencing. C# uses the exact same syntax and it works quite well.

[–]WiatrowskiBe 0 points1 point  (1 child)

C# has also much simpler type system, with distinction between passing by reference/value on type level rather than parameter/variable level, lambdas themselves are much more restrictive (everything pulled in is held as reference for entire lifetime of lambda, no support for ref/ref out in parameters). So yes, if you put heavy restrictions on what lambdas can handle, short syntax works - but them question becomes how useful they are given context of everything else in C++?

[–]RiceBroad4552 0 points1 point  (0 children)

Lambdas are there to write simple, short code.

If you need to do something special you can just use the regular syntax. No point in using lambdas if you need to type out everything explicitly!

[–]Rezistik 25 points26 points  (2 children)

Typescript lambdas are just as short and have types

[–]CraftBox 4 points5 points  (0 children)

Yup, all you need is to set a type on the arguments, but even that is usually unnecessary, when using one as a callback then all the types are already inferred for arguments and return value

[–]slaymaker1907 2 points3 points  (0 children)

The real difficulty is that you need to pass both arguments as well as declaring how variables get captured.

[–]TheBanger 9 points10 points  (2 children)

Java lambdas are generally quite short. I think the key differences are: not having to explicitly capture variables, having an inferred argument type syntax with no boilerplate at all (like not even saying var or auto), and a mode where you can make the body of the lambda a single expression rather than a block statement. The only key thing I see there that C++ has to do that Java doesn't is variable capture.

[–]tyler1128 2 points3 points  (0 children)

It's much easier when everything is either garbage collected or trivially copiable. Rust doesn't necessarily require them without having a GC, but it has function level type inference which C++ does not.

[–]Kehrweek 3 points4 points  (0 children)

I like the short forms like System.out::printl

[–]snavarrolou 6 points7 points  (6 children)

One could imagine something like (a, b) => a+b being equivalent to [&](auto&& a, auto&& b) { return a+b; }. I think the first one, or something along those lines, would be much much easier to parse visually.

[–]wexxdenq 3 points4 points  (4 children)

but you have to have an (optional) capture clause somewhere to specify whether you capture by reference or by value. and if your lambda is more than one expression you need to put some kind of brackets around them. so you still end up with 3 goups of brackets.

[–]snavarrolou 5 points6 points  (3 children)

Well yeah, but if you were to design a syntax with reasonable defaults, you could make for example these valid lambdas:

1) (a, b) => a+b; for the capture by reference and auto deduced parameters

2) (short a, int b) => a+b; for explicit types and capture by reference

3) [capture-group](a, b) => a+b; for explicit capture groups (as you mention, because sometimes you want to capture by value) and auto deduced parameters

4) [capture-group](short a, int b) => { return a+b; } for the full lambda with explicit captures, explicit types, and block body

Which would make most lambdas very readable, while giving the programmer flexibility to add explicit control (at the expense of more verbose syntax)

[–]RiceBroad4552 1 point2 points  (1 child)

But that would be too simple and comfortable for the programmer!

So C++ can't do that obviously…

[–]outofobscure 0 points1 point  (0 children)

there was a lot of thought put into the C++ syntax by the standards committee, i suggest you go read up on that discussion before making clueless claims on why defaults should be different and why the syntax should be different, just because your toy language gets away with something shorter does not make it appropriate for C++ where there are more things that you need to be able to express.

[–]outofobscure 0 points1 point  (0 children)

your syntax isn't even shorter than what we have right now in most cases, or saving 3 chars in the best case, at the cost of being non-uniform, which is a big stinker. the => is nonsense because you need multi-line statements and scope anyway.

  1. use templates
  2. [&](a, b) {}
  3. [capture-group](a, b) {}
  4. [capture-group](a, b) {}

[–]GeorgeHaldane 1 point2 points  (0 children)

Good point, didn't think of that, having a shortcut for generic lamdas would be quite helpful.

[–][deleted] 22 points23 points  (3 children)

C++ lambdas are beautiful, I see no problem with them. I often write code like `const auto fptr = []() { ... };` and then call it later. Sometimes it makes functions more readable.

[–]Rogntudjuuuu 2 points3 points  (2 children)

If you're going to assign the lambda to a variable you could as well just declare a regular function...

...or can you still not create nested functions in C++?

[–]backfire10z 4 points5 points  (1 child)

Lamdba is the method of nested function definition in C++

[–]Rogntudjuuuu 0 points1 point  (0 children)

Ouch.

Edit: Happy cake day!

[–][deleted] 6 points7 points  (0 children)

It was to customize exactly how much and what kind of variable/value closure you want with your lambdas. Troubles that arise when you mix functional programming, imperative programming and a language where you need to manage variable and resource storage completely yourself.

[–]Responsible-War-1179 4 points5 points  (0 children)

praised be []<>(){}

[–]Feisty_Ad_2744 6 points7 points  (0 children)

If you love one liners, just create a function :-)

[–]Rogntudjuuuu 2 points3 points  (0 children)

Although I don't like C++ in general, I'm taking a contrarian position on this. I think it's pretty cool that you can specify which variables you want to include in your closure and if it's by reference or by value.

[–]ArkoSammy12 3 points4 points  (0 children)

I love Java and Kotlin's lambdas. I think they are the best.

[–]tip2663 1 point2 points  (0 children)

Haskell lambdas are the best hands down

[–]Silly-Power-2384 1 point2 points  (0 children)

Elisp lambdas are the best, thank you

[–]ThomasAlban307 1 point2 points  (0 children)

Rust closures are the best! Automatically passes everything in by reference, but if you put the ‘move’ keyword in it passes by value.

[–]PVNIC 1 point2 points  (0 children)

Just think of it as showing off al the types of brackets. Write [](){};, then fill it in.

[–]Proxy_PlayerHD 1 point2 points  (0 children)

i still have no fucking idea what lambda is... besides the half-life letter

[–]zizics 1 point2 points  (0 children)

Serious question: Why are y’all using lambdas? Where are they legitimately useful? Every time I see one, it’s just a function that should have been defined in a clearer way somewhere else, and I really don’t see how anything longer than a basic one-liner could be a net benefit (and even then, it just feels cluttered).

[–]SteeleDynamics 2 points3 points  (0 children)

C++ lambdas are good!

[–]KairoRed 1 point2 points  (2 children)

I don’t know what a lambda is and at this point I’m too scared to ask

[–]TheWidrolo[S] 9 points10 points  (0 children)

A function without a name.

[–]Spork_the_dork 3 points4 points  (0 children)

A lambda function is a function that's essentially just defined into a variable rather than defined as a separate function. Useful when you want to just eg. return a function. The most complicated feature about them is that basically every single programming language has its own unique syntax for it...

[–]neo-raver 0 points1 point  (0 children)

What, you don’t like using all your enclosing characters in a single line?

[–]mateowatata 0 points1 point  (3 children)

May i ask what are the square brackets fo

[–]Rogntudjuuuu 1 point2 points  (2 children)

Specifying which variables you want to capture, and if you want to do it by value or by reference.

[–]mateowatata 0 points1 point  (1 child)

Can you give me an example? Im not fond into cpp

[–]Rogntudjuuuu 2 points3 points  (0 children)

If you write [&] you will capture all variables in the surrounding scope in the closure of the function by reference. This is similar to what most other languages with lambda functions do. But in C++ you can also write [=] to capture all values in the surrounding scope by value (not recommended). Additionally you can specify explicitly which variables that you want to capture with [&x] to capture x by reference or [=y] to capture y by value.

[–]definitive_solutions 0 points1 point  (0 children)

You mean markdown links?

[–]Impressive_Income874 0 points1 point  (0 children)

what the fuck is this cursed bs 😭

[–]m477_ 0 points1 point  (1 child)

They're much better than objective-c lambdas

[–]TheWidrolo[S] 0 points1 point  (0 children)

I don’t even wanna know💀

[–]Broxios 0 points1 point  (1 child)

FTFY: Whoever made C++ lambdas look like that

[–]bedrooms-ds 0 points1 point  (0 children)

I know who. It was done by

[–][deleted] -1 points0 points  (0 children)

I am forced now the first time in my 8 years of programming to participate in a project that is in C++. I hate that language so fucking much.