Created
October 26, 2012 18:02
-
-
Save splinterofchaos/3960343 to your computer and use it in GitHub Desktop.
Fmap in C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <utility> | |
#include <iostream> | |
#include <algorithm> | |
#include <iterator> | |
struct sequence_tag {}; | |
struct pointer_tag {}; | |
template< class X > | |
X category( ... ); | |
template< class S > | |
auto category( const S& s ) -> decltype( std::begin(s), sequence_tag() ); | |
template< class Ptr > | |
auto category( const Ptr& p ) -> decltype( *p, p==nullptr, pointer_tag() ); | |
template< class T > struct Category { | |
using type = decltype( category<T>(std::declval<T>()) ); | |
}; | |
template< class R, class ... X > struct Category< R(&)(X...) > { | |
using type = R(&)(X...); | |
}; | |
template< class T > | |
using Cat = typename Category<T>::type; | |
template< class... > struct Functor; | |
template< class F, class FX, class Fun=Functor< Cat<FX> > > | |
auto fmap( F&& f, FX&& fx ) | |
-> decltype( Fun::fmap( std::declval<F>(), std::declval<FX>() ) ) | |
{ | |
return Fun::fmap( std::forward<F>(f), std::forward<FX>(fx) ); | |
} | |
template< class F, class G > | |
struct Composition { | |
F f; | |
G g; | |
template< class X > | |
auto operator () ( X&& x ) -> decltype( f(g(std::declval<X>())) ) { | |
return f(g(std::forward<X>(x))); | |
} | |
}; | |
// General case: composition | |
template< class Function > struct Functor<Function> { | |
template< class F, class G, class C = Composition<F,G> > | |
static C fmap( F f, G g ) { | |
C( std::move(f), std::move(g) ); | |
} | |
}; | |
template<> struct Functor< sequence_tag > { | |
template< class F, template<class...>class S, class X, | |
class R = typename std::result_of<F(X)>::type > | |
static S<R> fmap( F&& f, const S<X>& s ) { | |
S<R> r; | |
r.reserve( s.size() ); | |
std::transform( std::begin(s), std::end(s), | |
std::back_inserter(r), | |
std::forward<F>(f) ); | |
return r; | |
} | |
}; | |
template<> struct Functor< pointer_tag > { | |
template< class F, template<class...>class Ptr, class X, | |
class R = typename std::result_of<F(X)>::type > | |
static Ptr<R> fmap( F&& f, const Ptr<X>& p ) | |
{ | |
return p != nullptr | |
? Ptr<R>( new R( std::forward<F>(f)(*p) ) ) | |
: nullptr; | |
} | |
}; | |
int main() { | |
auto neg = [](int x){return -x;}; | |
std::unique_ptr<int> p( new int(5) ); | |
p = fmap( neg, fmap( neg, p ) ); | |
std::cout << "-5 = " << *p << std::endl; | |
std::vector<int> w = { 1, 2, 3 }; | |
w = fmap( neg, w ); | |
std::copy( std::begin(w), std::end(w), | |
std::ostream_iterator<int>(std::cout," ") ); | |
std::cout << std::endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
line 86 should be std::cout << "5 = " << *p << std::endl; since you're applying twice the function :)