6 ms·
`std::flip`
- fooker 1y agoC++ is surprisingly close to being a usable functional language. The two missing pieces are - * structural pattern matching * uniform function call syntax that is : a.foo(b) vs foo(a, b) being interchangeable. With the kitchen sink approach of design I’d not be surprised if these get into the language eventually. These ideas have been proposed a few times but haven’t been seriously considered as far as I know.
- ashvardanian 1y agoThe second piece (uniform call syntax) looks convenient, though I don’t see a realistic way to integrate it into modern C++. The first (structural pattern matching) is, for me, more of a dividing line between low- and high-level languages. I tend to avoid it in my C++, just as I avoid inheritance, virtual functions, and exceptions… or `<functional>` header contents. Still, it’s always fun to stumble on corners of the STL I’d never paid attention to, even if I won’t end up using them. Thought it was worth sharing :)
- fooker 1y agoThere was an experimental implementation for uniform function call syntax in a clang fork, so it’s clearly doable.
- xigoi 1y ago> With the kitchen sink approach of design I’d not be surprised if these get into the language eventually. Based on the history of C++, they will, but with extremely bizarre syntax. Instead of a.foo(b), it will be something like a@<foo>::&{b}.
- im3w1l 1y agoIsn't structural pattern matching basically the same as creating an anonymous struct whose fields are references, with a default assignment operator? Syntax ideas should spring to mind.
- vjvjvjvjghv 1y ago“ a@<foo>::&{b}” This made me smile. So true
- pjmlp 1y agoSee new reflection syntax.
- jandrese 1y agoWith every new version C++ takes one step closer to Rust.
- xigoi 1y agoRust syntax is ugly because of C++. They didn’t want to “spend their innovation tokens”, which apparently means copying the worst existing design just because it’s popular.
- p0w3n3d 1y agoBut Java also did it with optimizing perform(a -> a.add(1)) with perform(VeryLongAndComplicatedAddingIntegerFacade::add)
- p0w3n3d 1y agoSo true. For me C++ syntax is unreadable, but the ideas behind it are familiar
- kelseyfrog 1y agoThe justification will, of course, be that the other options break digraph support on some ancient platform that has no public spec and will never sunset - likely one IBM AS/400 in a basement with a 45 year uptime. Out of "respect for existing deployments," the syntax must accommodate the relic even though no one has seen it outside of the folklore of one 1993 Usenet post. Every tool chain will begrudgingly implement it and then years later when someone proposes removing it the counter argument will be, "we can't remove it now someone is using it!" The someone, of course, is a hobbyist on the mailing list whose project depends critically in the feature.
- bongodongobob 1y agoWell, they're still in use. I've encountered 3 in the last 5 years of consulting. Just because your startup doesn't have one doesn't mean they don't exist in the manufacturing world. Are they ubiquitous? No. Are they rare? Depends on the industry.
- kelseyfrog 1y agoPerfect! The standards committee will be able to reference this reply the next time they justify eye-bleeding syntax instead of having to go mine Usenet. Thank you.
- chris_wot 1y agoAnd the standards committee will be correct.
- wat10000 1y agoWhen you say they're still in use, do you mean AS/400s or digraphs?
- bongodongobob 1y agoAS/400s
- deleted 1y ago[deleted]
- delta_p_delta_x 1y ago> * structural pattern matching https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2688r5.html#comparison-tables https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p26...
- zahlman 1y agoI don't find it surprising. My impression is that people like Herb Sutter and Alexander Stepanov actively pushed in that direction in the early days. `<functional>` was, AFAIK, part of the STL before it got incorporated into the C++ standard library.
- jcranmer 1y ago> * uniform function call syntax that is : a.foo(b) vs foo(a, b) being interchangeable. Herb Sutter has proposed this: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p3021r0.pdf https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p30... (twice, even, there was an older version of the paper several years ago that didn't pass). I'm resolutely opposed to such a thing because, having had to actually wade through C++'s name lookup and overload resolution rules in the past, they're a dense, confusing morass of logic that you can only stand to stare at for a half-hour or so before your brain turns to mush and you need to do something else, and anything that adds to that complexity--especially in "this makes things two things nearly equivalent"--is just a bad idea. (For an example of C++ overload resolution insanity, consider this:) // Given these overloads... void f(std::float32_t); void f(double); // Which one does this line call? Assume float/double are standard IEEE-754 types. void f((float)1);
- billforsternz 1y agoI'm going to guess f(double) because floats have always been promoted to doubles for function calls since K&R. But I'm not sure by any means. I'd be ready to get more explicit if I needed some specific behaviour.
- tredre3 1y agoBy mentioning K&R you seem to imply that C also promotes floats to doubles in function calls? But that is not the case, floats are passed as floats, as you'd expect. You can try it yourself on godbolt all the way back to GCC 3, test(float x) has always emitted movss and test(double x) will result in movsd/movlpd.
- jcranmer 1y agoUnless you're calling a variable-argument function--floats are promoted to doubles for variable-argument functions: > The arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter, if present. The integer promotions are performed on each trailing argument, and trailing arguments that have type float are promoted to double. These are called the default argument promotions. No other conversions are performed implicitly
- codeflo 1y agoAchieving uniform call syntax is easy, compilers just need to implement a new form of symbol resolution called "Kaiser lookup". It follows 14 easy to understand rules. It first looks up methods, then searches for template definitions in /tmp, then for a matching phase of any Saturn moon at time of compilation, then looks of definitions std::compat, and then in the namespaces of the types of any variables in scope anywhere on the call stack. If none of those work, it tries to interpret the call syntax as a custom float literal, and if even that fails, as a Perl 4 compatible regex. It's really intuitive if you think about it.
- constantcrying 1y agoAnother missing piece is a good syntax. C++ has, as you said, most of the capabilities already, but actually using them will quickly turn the code into symbol vomit.
- xigoi 1y ago“C++” and “good syntax” is a contradiction.
- nextos 1y agoThere are actually a few functional programming in C++ books out there. The language has changed a lot since C++98, when this would be unthinkable. Alexander Granin maintains a curated list of functional programming C++ resources [1]. [1] https://github.com/graninas/cpp_functional_programming https://github.com/graninas/cpp_functional_programming
- andyjohnson0 1y ago> uniform function call syntax that is : a.foo(b) vs foo(a, b) being interchangeable. Ive written a lot of c++ in the past but I'm not particularly knowledgeable about fp, so I'm wondering why this is important. Is it syntactic sugar or something more significant?
- fredrikholm 1y agoIt allows existing method-heavy code to be used in a functional style without bending the knee to more OOP inspired patterns. Think of a fluid API, but instead of chaining method calls you'd pass data to several functions "chained" together similar to how UNIX pipes work. With this type of API, you can pass one argument into the function and pipe the other such that: data |> foo(bar) |> baz Is a more FP friendly version of: return baz(foo.bar(data))
- andyjohnson0 1y agoMakes sense. Thank you!
- warkdarrior 1y agoShouldn't that be? data |> bar(foo) |> baz Or maybe: data |> bar(&foo) |> baz
- fredrikholm 1y agoIn Clojure it's: (.-> foo bar baz) So you might be right in correcting me, it's been a while since I've used another FP language in anger. With regards to taking the address of foo, pointers are generally not a (user space) concept in FP languages. The compiler/runtime usually optimizes cases like this as passing function pointers is a very large corner stone of FP. For mutation semantics you often approach it similar to how atomics works in non-FP languages. When opting into mutation you lose one of the pillars of FP which is idempotency and purity via immutability. Treating it as a special case helps scope it down to "here be dragons" areas.
- fooker 1y ago
- Sharlin 1y ago> * structural pattern matching Yes, but that has to come with proper sum types. std::variant doesn’t quite cut it. I’d also absolutely require a simpler lambda syntax. The current one is terrible for one-liner lambdas.
- fooker 1y agoC++ has a philosophy of not doing anything with the language if it can be done in library. Now the question is : what can we improve in the language so it can allow you to define a sum type better and more usable compared to std::variant? This is a surprisingly difficult question to answer, hence we haven’t had progress there.
- MattPalmer1086 1y ago> C++ has a philosophy of not doing anything with the language if it can be done in library. Then why is the language so ridiculously complicated? I had the most fun working with it back in around 2000, and even then it was quite insane. A lot has been added since then.
- aw1621107 1y agoPreferring things to be done in a library is not mutually exclusive with adding features that require language support as well.
- fooker 1y ago> Then why is the language so ridiculously complicated? Mainly as an effort to not drop compatibility with 30 years old code. You could cut the C++ standard in half if not for this.
- xigoi 1y ago> C++ has a philosophy of not doing anything with the language if it can be done in library. Except all the OOP features, which absolutely needed dedicated syntax even though they could have been a library.
- hackyhacky 1y ago> The two missing pieces are - For three decades, many people have said "C++ is nearly usable, except for just one thing..." Many of those people have gotten their way, which is why C++ is now a kitchen sink of mutually inoperable, poorly thought-out features, like someone implemented the entire index from Journal of Programming Language Design. If you don't believe me, check out the implementation of `flip` at the end of the article. Over a hundred lines of templates, variadic parameters, constexpr, r-value references, parameter forwarding, default and explicit constructors, and other things that, imho, simply should not exist in a modern programming language. So, instead, I'd say the best thing for C++, and the world, would, at this juncture to stop adding features.
- jcelerier 1y agoso how does flip looks like in your favourite language?
- hackyhacky 1y agoIn Haskell it's: flip :: (a -> b -> c) -> b -> a -> c flip f x y = f y x A little better, I'd say. Note that the first line is the type signature and is technically optional. EDIT: and if that's too exotic for you, here's the solution in Python. def flip(x): return lambda *z: x(*reversed(z))
- fooker 1y agoYou misunderstand why the C++ version is complex. Here's a similar version in C++ that is a one liner too. https://godbolt.org/z/dhsdoGcc4 https://godbolt.org/z/dhsdoGcc4 Notably, examine the simple assembly generated. In comparison, most other languages doing this will not be able to optimize away these abstractions at all.
- hackyhacky 1y agoI understand just fine why the C++ version is complex: it's because C++ is a poor language for higher-order function abstractions. Please don't make excuses for C++'s excessive inessential complexity: there is no reason why a programmer should need to concern themselves with "const r-values" and similar shenanigans in order to accomplish this simple task. The fact that the compiler is able to optimize away this code is a compiler issue, not a language issue. ghc will optimize away Haskell's flip and I didn't once have to write a compile-time index-reverser in template code.
- p0w3n3d 1y agothe best feature I love in python is: s = someobj() ref1 = s.doOne ref2 = s.doTwo // then later ref1(arg1,arg2) ref2() In Java you need to code like a crazy to have similar results. Also m = someclas.doThree // later o = someclas() o2 = someclas() for i in [o,o2]: m(i) so many patterns done easy and the right way
- abalaji 1y agothis blog post will be a great barometer of commenters who read the post vs those who don't
- jeffbee 1y agoTHUITFHNGL Fortunately almost all the functional features in the article, like range folds and negation wrappers, do exist.
- mindcrime 1y ago> THUITFHNGL Tragic Harvest Underwrites Infrastructure That Frobs Haptic Network Generation Load?
- deleted 1y ago[deleted]
- slater 1y agoWhen in doubt, UD it: https://www.urbandictionary.com/define.php?term=THUITFHNGL https://www.urbandictionary.com/define.php?term=THUITFHNGL
- mindcrime 1y agoOh I did. I just couldn't resist the temptation to have some fun with it. :-)
- scubbo 1y agoCan you help me out with an expansion of "TFA", when used to refer to the original post in comments? I simply don't believe that every commenter is using it to mean "The Fucking Article" in comments that are otherwise devoid of aggression or profanity.
- mindcrime 1y agoI can't speak for anybody else, but when I see "TFA" I do indeed translate it as "The Fucking Article". But if it makes you feel better you can think of it as "The Friggin' Article" or "The Fine Article" or something else less profane.
- arjvik 1y ago* Some standards such as ISO 6709 (Standard representation of geographic point location by coordinates) describe points as (latitude, longitude). * Others such as RFC 7946 (GeoJSON) describes points as (longitude, latitude). Using (hypothetical) std::flip to reify these APIs seems like a loaded footgun - someone is bound to accidentally use it {zero, two} times to convert between orders when it needs to be used once and wreak havoc.
- wk_end 1y agoNo more of a loaded footgun than trying to do it manually, I don't think. Geographic points should probably be represented as a labeled structure to prevent confusion and passed into functions as such. Using two separate libraries with mutually incompatible error-prone APIs as described is the real loaded footgun IMO. If you can't find better libraries, write wrappers; if you don't have time to write/maintain wrappers, pray. Anything else is just a bandaid.
- jandrewrogers 1y agoProbably no better or worse than the alternative. That aside, the example doesn't understand the standards in question. The governing standard for geospatial data representation is ISO 19125, which defines (longitude, latitude) order. GeoJSON naturally conforms to ISO 19125 since it is a format for processing data on computers. ISO 6709 is essentially a print formatting standard and orthogonal to storing geospatial data on computers. That some data file formats happen to be human readable does not make ISO 6709 apply. If you are processing geospatial data on computers the correct order is always (longitude, latitude).
- usefulcat 1y ago> someone is bound to accidentally use it {zero, two} times That risk is inherent to the problem at hand, and has nothing to do with std::flip.
- Dylan16807 1y agoYou can reduce the risk with types. If you use flip, you're not doing that, which makes flip not a very good solution here.
- cocoto 1y agoI love Haskell but when writing C++ I always avoid functional style gibberish. I feel like this style of programming only works in languages properly designed for that.
- eulgro 1y agoWhen C++23 is fully supported functional style will be somewhat bearable, but even then a lot is missing. Lambdas are just too verbose to make this fun.
- deleted 1y ago[deleted]
- zahlman 1y ago> Interestingly enough, most of these implementations only flip the first two parameters of whichever function they are passed, though it seems to be because most of them are based on the Haskell prelude, and handling arbitrary arity can be tricky in that language. Probably because the use case for it with higher arity is hard to imagine. (Indeed, TFA gives only examples with binary operations.) > Fortunately it is not just useless knowledge either: flip can be reified at will by copying the following C++17 implementation. > [snip 114 lines of code] Meanwhile, in Python: def flip(f): return lambda *args, **kwargs: f(*args[::-1], **kwargs) (I leave keyword arguments alone because there's no clearer semantic for "flipping" them.) The `toolz.functoolz.flip` implementation (being restricted to binary functions) is an even simpler one-liner (https://toolz.readthedocs.io/en/latest/_modules/toolz/functoolz.html#flip https://toolz.readthedocs.io/en/latest/_modules/toolz/functo...), though accompanied by a massive docstring and admittedly simplified through a heavyweight currying decorator (which accomplishes much more than simply getting a function that does the right thing).
- Jaxan 1y agoFor higher arity there is a combinatorial explosion of all the possible permutations. But if you want to flip the 2nd and 3rd argument in Haskell it can be done by flip itself: flip23 foo = (\x -> flip (foo x))
- marvinborner 1y agoOr just (flip .), which also allows ((flip .) .) etc. for further flips. In Smullyan's "To Mock a Mockingbird", these combinators are described as "cardinal combinator once/twice/etc. removed", where the cardinal combinator itself defines flip.
- Jaxan 1y agoI was hoping someone would provide something more succinct ;-). Thanks!
- Matheus28 1y agoYour python code allocates an array and inverts it every function call. The C++ code has no overhead and is equivalent to a compile time transformation.
- loeg 1y ago> Fortunately it is not just useless knowledge either: flip can be reified at will by copying the following C++17 implementation. I hope not.
- abrudz 1y ago⍨
- Matheus28 1y agoShould be using empty base optimization or [[no_unique_address]] for that implementation
- ratmeadow 1y agoVery soon after reading this I started to doubt my whole life's work in C++... std::flip? Never heard of it before, and I used to read cppreference for fun. And the name smells sus. I don't think the committee would name it "flip" if it did exist. Too short a name for something too niche. I looked at cppreference and saw that yep, this does not exist, so thought maybe this article was some kind of AI hallucination. Finally the author said it was all made up and my mind was at peace once more.
- loeg 1y ago> And the name smells sus. I don't think the committee would name it "flip" if it did exist. Too short a name for something too niche. Yeah, this would be std::reverse_bind() or something.
- jurschreuder 1y agoSmells sus? There is a pun-intended -sus- feature in Morwenn's popular sorting library. The more you know :susface:
- inetknght 1y agoFunny enough, I've known it exists. I've never really found much of a use case for it. Where I have found it "might" be useful is rare one-off APIs. If I were to write a wrapper for one API to work with an equivalent API, then maybe I might use this. But for one-off things, it's not important enough to use IMO. On the other hand, I remember reading it and thing it was a bit-flip operation since it's in <functional> instead of <tuple>. So I was quite surprised to find that it's really much more like a tuple operation (which is template magic) than bit flipping (at runtime, or possibly also constexpr)
- Valodim 1y agoWhat are you even talking about? std::flip does not exist, as asserted by both TFA and your parent comment. (ugh, it is unnerving how certain my expectation is that the answer will start with "you're absolutely right!")
- hackyhacky 1y agoThis seems like an obvious functional-style function that C++ should have, given the trend for functional idioms in recent versions. However, even a C++ cynic such as myself was shocked by the length and (to me) incomprehensible implementation at the end of the article. A definition of that complexity for something so simple tells me that something has gone off the rails. The equivalent function in Haskell, fwiw, can be implemented in its entirety as follows: flip :: (a -> b -> c) -> b -> a -> c flip f x y = f y x
- mcherm 1y agoThat DOES seem easy, but it's because the code you wrote only works for functions of two arguments. Can you write a Haskell version that works for functions with any number of arguments?
- rcxdude 1y agoDoesn't that version work with arbitrary numbers of arguments already thanks to currying?
- hackyhacky 1y agoSort of: the version I gave flips the first two parameters of a function, leaving the rest untouched. The C++ std::flip from the article, however, reverses the order of all parameters. Reproducing that in Haskell would require fancy type-level programming or compile-time metaprogramming, both of which would defeat the simplicity of my initial version.
- kazinator 1y ago> The LISP family of languages generally does not provide such a function by default The Lisp family doesn't provide anything but parentheses, maybe lambda (not necessarily under that name) and a whole lot of heated arguments. Only specific dialects of specific Lisp-family languages provide, or don't provide this and that: TXR Lisp: 1> (mapcar [flipargs -] '(0 1 2) '(10 20 30)) (10 19 28) flip is called flipargs because flip is the name of an operator that mutates a place with a negation of its current value: i.e (flip x) means (set x (not x)) with x evaluated once.