`std::flip`
Posted4 months agoActive3 months ago
morwenn.github.ioTechstoryHigh profile
skepticalmixed
Debate
80/100
C++Functional ProgrammingStd::flip
Key topics
C++
Functional Programming
Std::flip
The article proposes a hypothetical `std::flip` function for C++, sparking debate about its usefulness and potential implementation, with commenters weighing in on the merits of functional programming in C++.
Snapshot generated from the HN discussion
Discussion Activity
Very active discussionFirst comment
3d
Peak period
92
84-96h
Avg / period
18.7
Comment distribution112 data points
Loading chart...
Based on 112 loaded comments
Key moments
- 01Story posted
Sep 26, 2025 at 2:29 AM EDT
4 months ago
Step 01 - 02First comment
Sep 29, 2025 at 2:10 PM EDT
3d after posting
Step 02 - 03Peak activity
92 comments in 84-96h
Hottest window of the conversation
Step 03 - 04Latest activity
Oct 1, 2025 at 9:47 PM EDT
3 months ago
Step 04
Generating AI Summary...
Analyzing up to 500 comments to identify key contributors and discussion patterns
ID: 45383381Type: storyLast synced: 11/20/2025, 4:44:33 PM
Want the full context?
Jump to the original sources
Read the primary article or dive into the live Hacker News thread when you're ready.
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.
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 :)
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}.
This made me smile. So true
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.
Herb Sutter has proposed this: 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:)
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.
> 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
Ex: https://godbolt.org/z/TKjz3Tqqr
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?
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:
Is a more FP friendly version of: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.
data |foo> bar |> baz
This almost gets us to a point where you can express a dataflow DAG in code.
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.
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.
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.
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.
Except all the OOP features, which absolutely needed dedicated syntax even though they could have been a library.
You can design a pretty decent version if your users are willing to write a tiny bit of boilerplate.
In fact, the LLVM project sort of does this, for the sake of compiling with -fno-rtti
- https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html
I don’t really disagree with you, but consider that one of the main goals of an object oriented language is to allow you to define custom types.
OOP chose to implement "one of" with subclasses and runtime polymorphism, but that's not really "one of these choices" but "one of an arbitrarily large space of choices" because OOP interfaces are extensible by design. You can add support for "sealed" interfaces, but at that point you basically have tagged unions with a very clumsy syntax (and bad performance depending on your object model).
C, of course, has `union`, which is what you'd expect from C: a very basic, very error-prone building block for DIY tagged unions. C++ improved (depending on who you ask) on C structs by adding privacy control, methods, and so on. Similarly C++ should improve on C unions by adding implicit tagging, soundness, exhaustivity analysis and so on, so that nobody has to write `class X { enum { a_, b_ } tag; union { A a; B b; }; }` and all the associated ceremony ever again.
Like Haskell, you’d want different tags to be constructed by different constructors. But it’s unclear how this will integrate with destructors, RAII, and inheritance.
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.
EDIT: and if that's too exotic for you, here's the solution in Python.
Here's a similar version in C++ that is a one liner too.
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.
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.
Agreed, hence my one liner version.
> ghc will optimize away Haskell's flip
Can you show me how? I am seeing 5000 lines of assembly.
https://godbolt.org/z/KqTq5Ez5n
You can see this clearly if you use conspicuous numbers. If, instead of (flip foo 2 3), you give (flip foo 2 99), you will code like this:
97 is calculated at compile time.https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p26...
[1] https://github.com/graninas/cpp_functional_programming
Fortunately almost all the functional features in the article, like range folds and negation wrappers, do exist.
Tragic Harvest Underwrites Infrastructure That Frobs Haptic Network Generation Load?
https://www.urbandictionary.com/define.php?term=THUITFHNGL
Just reverse parameter order. It seems very silly.
https://github.com/protocolbuffers/protobuf/blob/main/src/go...
This library was written in the C++98 era. It might seem silly now because with C++11, we could use std::bind or lambda expressions instead.
https://cppreference.com/w/cpp/utility/functional/not_fn.htm...
If you don't see any value in that, you wouldn't see any value in the similar `flip` function or other combinators.
That risk is inherent to the problem at hand, and has nothing to do with std::flip.
The way to turn that into an even wronger solution is to create a helper just_apply(function, lat, long) that passes the arguments to the function in the (hopefully unique) order that successfully works; so flip if needed, based on function’s type signature.
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).
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.
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:
(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/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).
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))
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.
The C++ code has no overhead and is equivalent to a compile time transformation.
Yeah, this would be std::reverse_bind() or something.
The more you know :susface:
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)
(ugh, it is unnerving how certain my expectation is that the answer will start with "you're absolutely right!")
This is enough to determine it is written by Chatgpt.
If you're not familiar with Chatgpt, ChatGPT finds its roots in advanced machine learning research, particularly in the field of natural language processing (NLP), where it leverages deep learning models like GPT (Generative Pretrained Transformer) to understand, generate, and interact with human language based on vast amounts of data and context.
Can you write a Haskell version that works for functions with any number of arguments?
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.
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:
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.I hope not.