Recently I've been trying to create a C concept around Haskell's Functor. Something like this:
template <template<class>class F>
concept Functor = requires (F<A> f) {
{ fmap(function, f) } -> std::same_as<F<decltype(function(std::declval<A>))>>;
};
The problem is with function and A type. If I put them in template declaration than Functor concept depends on them not only on the actual functor F.
Is this sort of thing even possible in C .
I wish to make this code compile:
static_assert(Functor<std::optional<int>) where I already defined fmap on optional.
CodePudding user response:
Currently, as far as I see, that is not possible in the generality that you seek. The reason is that the all-quantifier over type arguments is not sensible in C because there will always be a lot of types/function combination for which a given template does not represent a Functor:
- function:
[]{}.std::optional<void>is not instantiatable. - type:
void. Similar problem:std::optionalis not a Functor for that. - type:
int&.std::optionalis not a Functor becausestd::optional<int&>does not exist.
Here is an approximation that illustrates the problem:
#include <concepts>
#include <optional>
template <typename T>
inline auto fmap(auto f, std::optional<T> opt)
-> std::optional<decltype(f(std::declval<T>()))> {
if (opt) return f(*opt);
return std::nullopt;
}
template <template <typename> typename F, typename T>
concept Functor = requires (F<T> f) {
{ fmap([](auto) {return nullptr;}, f) } -> std::same_as<F<decltype(nullptr)>>;
};
Here, the input function is fixed to a lambda that always maps to nullptr. The type for which the template shall be a Functor must be passed, so this:
static_assert(Functor<std::optional,int>);
passes, but this:
static_assert(Functor<std::optional,int&>);
does not.
Of course, this is unsatisfying because even if the fmap defined for std::optional works on [](auto) {return nullptr;} it doesn't mean it will work on the next function.
