[HN Gopher] `std::flip`
       ___________________________________________________________________
        
       `std::flip`
        
       Author : ashvardanian
       Score  : 75 points
       Date   : 2025-09-26 06:29 UTC (3 days ago)
        
 (HTM) web link (morwenn.github.io)
 (TXT) w3m dump (morwenn.github.io)
        
       | fooker wrote:
       | C++ 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 wrote:
         | The 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 wrote:
           | There was an experimental implementation for uniform function
           | call syntax in a clang fork, so it's clearly doable.
        
         | xigoi wrote:
         | > 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}.
        
           | vjvjvjvjghv wrote:
           | " a@<foo>::&{b}"
           | 
           | This made me smile. So true
        
             | pjmlp wrote:
             | See new reflection syntax.
        
           | p0w3n3d wrote:
           | So true. For me C++ syntax is unreadable, but the ideas
           | behind it are familiar
        
           | im3w1l wrote:
           | Isn'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.
        
           | kelseyfrog wrote:
           | The 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 wrote:
             | Well, 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 wrote:
               | Perfect! 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.
        
               | wat10000 wrote:
               | When you say they're still in use, do you mean AS/400s or
               | digraphs?
        
               | bongodongobob wrote:
               | AS/400s
        
         | delta_p_delta_x wrote:
         | > * structural pattern matching
         | 
         | https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p26...
        
         | zahlman wrote:
         | I 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 wrote:
         | > * 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/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 wrote:
           | I'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 wrote:
             | By 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 wrote:
               | Unless 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
        
               | DSMan195276 wrote:
               | It's a difference of whether the function arguments are
               | declared or not. If you declare a `void foo()`, and then
               | call `foo((float)f)`, the `foo()` function is actually
               | passed a `double` as the first argument rather than a
               | `float`. If you instead change the declaration to `void
               | foo(float)` then it gets passed as a `float`.
               | 
               | Ex: https://godbolt.org/z/TKjz3Tqqr
        
           | Sharlin wrote:
           | I think there have been proposals to add universal call
           | syntax since before C++98, some of them by Stroustrup
           | himself.
        
         | codeflo wrote:
         | Achieving 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.
        
           | jcranmer wrote:
           | I wish C++ name lookup and overload resolution were that
           | simple.
        
             | pjmlp wrote:
             | Yep, and there are new ways with modules, and reflection,
             | we can't have enough. :)
        
         | constantcrying wrote:
         | Another 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.
        
         | nextos wrote:
         | There 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
        
         | andyjohnson0 wrote:
         | > 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 wrote:
           | It 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 wrote:
             | Makes sense. Thank you!
        
             | warkdarrior wrote:
             | Shouldn't that be?                   data |> bar(foo) |>
             | baz
             | 
             | Or maybe:                   data |> bar(&foo) |> baz
        
             | fooker wrote:
             | Thinking about this a bit more, foo could be a 'context'
             | object here!
             | 
             | data |foo> bar |> baz
             | 
             | This almost gets us to a point where you can express a
             | dataflow DAG in code.
        
         | Sharlin wrote:
         | > * 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 wrote:
           | C++ 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 wrote:
             | > 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.
        
           | monkeyelite wrote:
           | How do you think sum types are implemented in functional
           | languages?
        
       | abalaji wrote:
       | this blog post will be a great barometer of commenters who read
       | the post vs those who don't
        
         | jeffbee wrote:
         | THUITFHNGL
         | 
         | Fortunately almost all the functional features in the article,
         | like range folds and negation wrappers, do exist.
        
           | mindcrime wrote:
           | > THUITFHNGL
           | 
           | Tragic Harvest Underwrites Infrastructure That Frobs Haptic
           | Network Generation Load?
        
             | slater wrote:
             | When in doubt, UD it:
             | 
             | https://www.urbandictionary.com/define.php?term=THUITFHNGL
        
               | mindcrime wrote:
               | Oh I did. I just couldn't resist the temptation to have
               | some fun with it. :-)
        
         | forrestthewoods wrote:
         | I skimmed the post. I have absolutely no idea what std::flip is
         | supposed to do. All the sample code looks awful and
         | undesirable. And that's coming from someone who writes C++
         | every day. Yes I read the plot twist at the end, made me lol.
        
           | loeg wrote:
           | > I have absolutely no idea what std::flip is supposed to do.
           | 
           | Just reverse parameter order. It seems very silly.
           | void f(int, double);            void main() {
           | flip(f)(3.14, 1);       }
        
             | gblargg wrote:
             | You do realize it's not meant for silly situations like
             | that, right?
        
           | OskarS wrote:
           | You write C++ every day, and you didn't understand the
           | is_descendant_of/is_ancestor_of example? Or how you can use
           | it to reverse a relation like std::less?
        
             | forrestthewoods wrote:
             | I don't understand why I should care about this. It doesn't
             | appear to solve real problems. The examples are all dumb
             | toys and simply writing a wrapper by hand is perfectly fine
             | and easier to read.
        
               | Jtsummers wrote:
               | It's as useful (or not) as you find this:
               | 
               | 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.
        
       | arjvik wrote:
       | * 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 wrote:
         | No 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 wrote:
         | Probably 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 wrote:
         | > 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.
        
       | cocoto wrote:
       | I 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.
        
       | zahlman wrote:
       | > 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.rea
       | dthedocs.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 wrote:
         | For 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 wrote:
           | Or 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 wrote:
             | I was hoping someone would provide something more succinct
             | ;-). Thanks!
        
         | Matheus28 wrote:
         | Your 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.
        
           | zahlman wrote:
           | Of course. But if I had to care about things on that level,
           | and I was willing to sit through the C++ compilation process
           | (and everything else that goes along with that), I wouldn't
           | be using Python in the first place.
        
       | loeg wrote:
       | > 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 wrote:
       | [?]
        
       | Matheus28 wrote:
       | Should be using empty base optimization or [[no_unique_address]]
       | for that implementation
        
       ___________________________________________________________________
       (page generated 2025-09-29 23:01 UTC)