2

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?

Bord81
  • 525
  • 2
  • 8
  • 23

3 Answers3

2

The issue is that template deduction works with exact match on type, without conversions.

You are passing in function pointers, which is not the same type as std::function, so deduction of the template parameters will fail.

The correct way is to take in the callables as template arguments. This ensures deduction will work. A lot of times you don't need to check the signature of the callable, since if it's used in the function you will get a compile time error if it's used in the wrong way.

If you still want to check the signature it's not very hard to do with a type trait.

#include <string>

template<class A, class B>
B fmap(A a, B b) {
    return a(b(std::string{}));
}

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

fmap(function_2, function_1);
super
  • 12,335
  • 2
  • 19
  • 29
2

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;
}
Alex
  • 877
  • 5
  • 10
0

Bartosz Milewski's book "Category Theory for Programmers" (2014-19) https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/ gives an example of the Writer functor in C++ ... and it's a simpler step from there to produce the Reader functor:

#include <string>
#include <functional>
using namespace std;

template<class R, class A, class B>   
function<B(R)>    Reader(function<A(R)> m1, function<B(A)> m2)   
{
       return [m1,m2] (R r) { return m2(m1(r)); };
}

// example
string repeat(string x) {return x+x;}
string i_to_s(   int x) {return to_string(x);}

string process(int x) {
             return Reader<int, string, string>(i_to_s, repeat)(x);}
Paul McGee
  • 102
  • 2
  • 7