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?
Just reverse parameter order. It seems very silly.
void f(int, double);
void main() {
flip(f)(3.14, 1);
}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.
* 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.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.
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).
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.
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/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.
#include <functional>
constexpr auto flip(const auto& f) {
return std::bind(f, std::placeholders::_2, std::placeholders::_1);
}
The best I could get the fully general version is still pretty obtuse though: // std doesn't have a template version of placeholders::_1, _2, etc., so we need to
// define our own.
template <int I> struct placeholder{};
template<>
template<int I>
struct std::is_placeholder<placeholder<I>> : std::integral_constant<int, I> {};
// flip must be an object so that the function type can be deduced without needing
// to explicitly specify its parameters' types.
template<typename F>
struct flip {
const F f;
// operator() deduces the argument types when the flip object is called, but really
// all we need to know is the number of arguments.
template<typename... Args>
constexpr auto operator()(Args... args) {
return bind_reversed(std::make_integer_sequence<int, sizeof...(Args)>{})(args...);
}
private:
// a helper function is needed to deduce a sequence of integers so we can bind all
// the placeholder values.
template<int... Is>
constexpr auto bind_reversed(std::integer_sequence<int, Is...>) {
return std::bind(f, placeholder<sizeof...(Is) - Is>{}...);
}
};I hope not.
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.
flip :: (a -> b -> c) -> b -> a -> c
flip f x y = f y xCan 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:
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.
fooker•4mo ago
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•4mo ago
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•4mo ago
xigoi•4mo ago
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•4mo ago
This made me smile. So true
pjmlp•4mo ago
jandrese•4mo ago
xigoi•4mo ago
p0w3n3d•4mo ago
p0w3n3d•4mo ago
im3w1l•4mo ago
kelseyfrog•4mo ago
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•4mo ago
kelseyfrog•4mo ago
chris_wot•4mo ago
wat10000•4mo ago
bongodongobob•4mo ago
MyOutfitIsVague•4mo ago
bongodongobob•4mo ago
cyphar•4mo ago
bongodongobob•4mo ago
esrauch•4mo ago
sagarm•4mo ago
delta_p_delta_x•4mo ago
https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p26...
zahlman•4mo ago
jcranmer•4mo ago
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:)
billforsternz•4mo ago
tredre3•4mo ago
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•4mo ago
> 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•4mo ago
Ex: https://godbolt.org/z/TKjz3Tqqr
billforsternz•4mo ago
Sharlin•4mo ago
jjmarr•4mo ago
codeflo•4mo ago
jcranmer•4mo ago
pjmlp•4mo ago
constantcrying•4mo ago
xigoi•4mo ago
nextos•4mo ago
[1] https://github.com/graninas/cpp_functional_programming
andyjohnson0•4mo ago
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•4mo ago
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:andyjohnson0•4mo ago
warkdarrior•4mo ago
fredrikholm•4mo ago
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•4mo ago
data |foo> bar |> baz
This almost gets us to a point where you can express a dataflow DAG in code.
Sharlin•4mo ago
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•4mo ago
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•4mo ago
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•4mo ago
fooker•4mo ago
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•4mo ago
Except all the OOP features, which absolutely needed dedicated syntax even though they could have been a library.
fooker•4mo ago
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
Sharlin•4mo ago
fooker•4mo ago
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.
Sharlin•4mo ago
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.
fooker•4mo ago
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.
monkeyelite•4mo ago
fooker•4mo ago
hackyhacky•4mo ago
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•4mo ago
hackyhacky•4mo ago
EDIT: and if that's too exotic for you, here's the solution in Python.
fooker•4mo ago
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.
hackyhacky•4mo ago
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.
fooker•4mo ago
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
hackyhacky•4mo ago
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.p0w3n3d•4mo ago