Much like this question I'm attempting to write in C++ something which can resemble Haskell's (.)
function as much as possible, which is basically Chapter 1's second exercise from Bartosz Milewski's Category Theory for Programmers.
This similar question about writing the identity function served as a reference too.
This answer on StackOverflow reassured me that decltype
used on lambdas' auto
parameters is equivalent to decltype
used on template parameters.
And this is what I came up with:
static constexpr struct compose_t {
template <typename F, typename G>
constexpr decltype(auto) operator()(F&& f, G&& g) const noexcept {
return [&f,&g](auto&& x){
return f(g(std::forward<decltype(x)>(x)));
};
}
} compose;
Some thoughts:
operator()
takesf
andg
by reference, and the returned lambda captures them by reference; and I made this choice thinking it should make passing non-stateless callables cheaper, but I'm not sure I made the right choice. Another reference is this.- Since I'm passing them by reference, I had to choose between
const&
and&&
, and I've basically chosen randomly. - I've not concerned myself with variadics, as I wanted a binary function composition "operator", in line with Haskell's
(.) :: (b -> c) -> (a -> b) -> a -> c
. - I cannot compose
compose
with something else, because it is binary, whereas the implementation implicitly assumes thatF
andG
be unary. I guess making something likecompose(compose,compose)
(which is(.).(.)
in Haskell) possible would be up to a partial application "facility".
This is a code where I tried to test it:
#include <cassert>
#include <utility>
// ... the code from above
static constexpr struct changesign_t {
template<typename T>
constexpr decltype(auto) operator()(T&& t) const noexcept { return -t; };
} changesign;
int square(int x) { return x*x; }
int twice(int x) { return 2*x; }
int main() {
assert(compose(square,twice)(3) == 36);
assert(compose([](int x){ return x*x; },[](int x){ return 2*x; })(3) == 36);
assert(compose(changesign, compose([](auto x){ return x*x; }, [](auto x){ return 2*x; }))(3) == -36);
}