Search code examples
c++templateshaskellfunctorcategory-theory

Reader functor in C++


I'm trying to implement a reader functor in C++. Corresponding Haskell definition is fmap :: (a -> b) -> (r -> a) -> (r -> b)

My C++ version is:

template<class A, class B, class R>
B fmap(const std::function<B(A)> &funcA, const std::function<A(R)> &funcR) {
    return funcA(funcR());
}

std::string function_1(int n);
double function_2(std::string s);

fmap(function_2, function_1);

The error is:

note: candidate template ignored: could not match 'function<type-parameter-0-1 (type-parameter-0-0)>' against 'double (std::__1::basic_string<char>)'

B fmap(const std::function<B(A)> &funcA, const std::function<A(R)> &funcR) {

What is the correct way to implement fmap function?


Solution

  • You can do this with a neat template conversion trick from Template type deduction with std::function

    #include <functional>
    #include <iostream>
    #include <string>
    using namespace std;
    
    template<class T>
    struct AsFunction
        : public AsFunction<decltype(&T::operator())>
    {};
    
    template<class ReturnType, class... Args>
    struct AsFunction<ReturnType(Args...)> {
        using type = std::function<ReturnType(Args...)>;
    };
    
    template<class ReturnType, class... Args>
    struct AsFunction<ReturnType(*)(Args...)> {
        using type = std::function<ReturnType(Args...)>;
    };
    
    
    template<class Class, class ReturnType, class... Args>
    struct AsFunction<ReturnType(Class::*)(Args...) const> {
        using type = std::function<ReturnType(Args...)>;
    };
    
    template<class F>
    auto toFunction(F f) -> typename AsFunction<F>::type {
        return { f };
    }
    
    template<class A, class B, class R>
    B fmap(const std::function<B(A)>& funcA, const std::function<A(R)>& funcR, R value) {
        return funcA(funcR(value));
    }
    
    template <class T>
    auto ToFunction(T t) {
        return t;
    }
    
    std::string function_1(int n) {
        return ""s;
    }
    
    double function_2(std::string s) {
        return 0.0;
    }
    
    int main() {
        fmap(toFunction(function_2), toFunction(function_1), 5);
        return 0;
    }