[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)