My question
I'm referring to a function which does essentially the following (modulo const
, &
, perfect forwarding, or whatever is appropriate):
auto constexpr dollar = [](auto f, auto x){ return f(x); }; // Why calling it "dollar"? Keep reading...
Is such a function expressable only via Boost.Hana?
Why did I think of it?
In Haskell, such a function exists, and it's called ($)
($
in infix form), and its definition is the following (source code)
($) :: forall r a (b :: TYPE r). (a -> b) -> a -> b
f $ x = f x
and you could write the second line simply as either of the following
(f $) = f
($) f = f
where the second form makes it apparent that ($)
is essentially the same as the id
(the identity function),
id :: a -> a
id x = x
just with a signature that enforces that the first argument be of function type a -> b
.
Indeed, applying f
to x
in Haskell can be done also by writing this
f `id` x
i.e. using `id`
instead of $
.¹
How is that related to Hana?
Since Hana does offer an id
function, I was wondering if that (maybe together with something else) can be used to define a function application utility without manually writing the lambda at the top of this post.
The difficult part
The hard part here is that when you write f `id` x
in Haskell, there's not much really a point in arguing on whether you're passing 1 or 2 arguments to id
, because all functions are curried by default.
That's not true in C++. For instance I can do this:
#include <boost/hana/functional/id.hpp>
#include <iostream>
using boost::hana::id;
int main() {
auto plus1 = [](int x){ return x + 1; };
std::cout << id(plus1)(3) << std::endl; // prints 4
}
which looks a lot like id
is curried and is being given two inputs one after the other rather than together, but that's not true. It's just that id(plus1)
is returning plus1
, which is fed with 3
. I don't know how to get the following (which would be equivalent to plus1 `id` 3
or id plus1 3
in Haskell) work:
std::cout << id(plus1, 3) << std::endl; // doesn't even compile obviously
The true origin of the puzzle
After reading To Mock a Mockingbird, I wondered: "How do I implement the Thrush in C++ only with Boost.Hana?" (And the Thrush is the boost::hana::flip
ped version of the function application operator.)
¹In reality it's not exactly the same if want to write chains of applications, as the two operators have different associativity, so f $ g $ x == f `id` (g `id` x)
, but this is not relevant to the question, I believe.
Eureka!
Here's a oneliner that seems to be a very succint solution:
where only Hana functions are used, namely
I haven't tested it thoroughly, but at least for some simple case it seems to work:
Having defined
identity
like above, thethrush
is easy to implement:(Compiler Explorer)
The
identity
function defined above, is very similar to this, modulo perfect forwarding and other details (which might become important when one tries toapply
member functions),