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::optional
is not a Functor for that. - type:
int&
.std::optional
is 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.