I want a Functor concept in C++20.
A functor is a higher-kinded type that can be mapped over. A simple example is std::optional
; with a function from type A
to type B
and a std::optional<A>
, you can easily create a std::optional<B>
by applying the function to the value if it exists and returning an empty optional
otherwise. This operation is called fmap
in Haskell.
template<typename A, typename B>
std::optional<B> fmap(std::function<B(A)> f, std::optional<A> fa) {
if (!fa) {
return std::optional<B>{};
}
return std::optional<B>(f(*fa));
}
A concept for all functors is simple enough to write. I've come up with this (using GCC—you'll have to remove bool
to get this working in Clang, I think):
template<template<typename> typename F, typename A, typename B>
concept bool Functor = requires(std::function<B(A)> f, F<A> fa) {
{ fmap(f, fa) } -> F<B>;
};
And a simple additional function to make sure this works:
template<typename A, typename B>
std::function<B(A)> constant(B b) {
return [b](A _) { return b; };
}
template<template<typename> typename F, typename A, typename B>
F<B> replace(B b, F<A> fa) requires Functor<F,A,B> {
return fmap(constant<A,B>(b), fa);
}
It works. But it's not pretty. What I want is for the signature of replace
to read like so:
template<Functor F, typename A, typename B>
F<B> replace(B b, F<A> fa);
No need for a requires-clause here. Much nicer, don't you agree? To get this to work, however, I'd have to reduce the template on my concept to a single argument. Something like this:
template<template<typename> typename F>
concept bool Functor = requires(function<B(A)> f, F<A> fa) { // Uh-oh
{ fmap(f, fa) } -> F<B>;
};
Problem is, I've not declared types A and B. As far as I can tell, there's nowhere I can declare them before I have to use them. Can what I want be done, and can it be done simply and elegantly?
One possible solution that comes to my mind is to make the requires-expression in the concept a template (or at least a template-like thing). I'd then have something like this:
template<template<typename> typename F>
concept bool Functor = requires<typename A, typename B>(function<B(A)> f, F<A> fa) {
{ fmap(f, fa) } -> F<B>;
};
Unfortunately, this is not valid by the C++20 standard and will not compile with g++-8
. Could something like this be viable? Could it make it into the standard?
See Question&Answers more detail:
os