mpc
Haskell-like feature supports in C++
applicative.hpp
Go to the documentation of this file.
1
2#pragma once
8#include <mpc/type_traits.hpp>
9
10// clang-format off
11
12namespace mpc {
13 // applicative
14 // https://hackage.haskell.org/package/base-4.16.0.0/docs/Control-Applicative.html#t:Applicative
15
17 template <class>
19
21 template <class F>
22 concept applicative_traits_specialized = requires {
23 applicative_traits<std::remove_cvref_t<F>>::pure;
24 applicative_traits<std::remove_cvref_t<F>>::seq_apply;
25 applicative_traits<std::remove_cvref_t<F>>::liftA2;
26 applicative_traits<std::remove_cvref_t<F>>::discard2nd;
27 applicative_traits<std::remove_cvref_t<F>>::discard1st;
28 };
29
31 template <class F>
33
34 // Methods required for the class definition.
35
36 namespace detail {
38 template <class F>
39 struct pure_op {
40 template <class A>
41 constexpr auto operator()(A&& a) const noexcept(
42 noexcept( applicative_traits<std::remove_cvref_t<F>>::pure(std::forward<A>(a))))
43 -> decltype(applicative_traits<std::remove_cvref_t<F>>::pure(std::forward<A>(a)))
44 { return applicative_traits<std::remove_cvref_t<F>>::pure(std::forward<A>(a)); }
45 };
46
51 struct seq_apply_op {
52 template <class Fab, class Fa>
53 constexpr auto operator()(Fab&& fab, Fa&& fa) const noexcept(
54 noexcept( applicative_traits<std::remove_cvref_t<Fab>>::seq_apply(std::forward<Fab>(fab), std::forward<Fa>(fa))))
55 -> decltype(applicative_traits<std::remove_cvref_t<Fab>>::seq_apply(std::forward<Fab>(fab), std::forward<Fa>(fa)))
56 { return applicative_traits<std::remove_cvref_t<Fab>>::seq_apply(std::forward<Fab>(fab), std::forward<Fa>(fa)); }
57 };
58
60 struct liftA2_op {
61 template <class Fn, class Fa, class Fb>
62 constexpr auto operator()(Fn&& fn, Fa&& fa, Fb&& fb) const noexcept(
63 noexcept( applicative_traits<std::remove_cvref_t<Fa>>::liftA2(std::forward<Fn>(fn), std::forward<Fa>(fa), std::forward<Fb>(fb))))
64 -> decltype(applicative_traits<std::remove_cvref_t<Fa>>::liftA2(std::forward<Fn>(fn), std::forward<Fa>(fa), std::forward<Fb>(fb)))
65 { return applicative_traits<std::remove_cvref_t<Fa>>::liftA2(std::forward<Fn>(fn), std::forward<Fa>(fa), std::forward<Fb>(fb)); }
66 };
67
73 template <class Fa, class Fb>
74 constexpr auto operator()(Fa&& fa, Fb&& fb) const noexcept(
75 noexcept( applicative_traits<std::remove_cvref_t<Fa>>::discard2nd(std::forward<Fa>(fa), std::forward<Fb>(fb))))
76 -> decltype(applicative_traits<std::remove_cvref_t<Fa>>::discard2nd(std::forward<Fa>(fa), std::forward<Fb>(fb)))
77 { return applicative_traits<std::remove_cvref_t<Fa>>::discard2nd(std::forward<Fa>(fa), std::forward<Fb>(fb)); }
78 };
79
85 template <class Fa, class Fb>
86 constexpr auto operator()(Fa&& fa, Fb&& fb) const noexcept(
87 noexcept( applicative_traits<std::remove_cvref_t<Fa>>::discard1st(std::forward<Fa>(fa), std::forward<Fb>(fb))))
88 -> decltype(applicative_traits<std::remove_cvref_t<Fa>>::discard1st(std::forward<Fa>(fa), std::forward<Fb>(fb)))
89 { return applicative_traits<std::remove_cvref_t<Fa>>::discard1st(std::forward<Fa>(fa), std::forward<Fb>(fb)); }
90 };
91 } // namespace detail
92
93 inline namespace cpo {
95 template <class F>
96 inline constexpr partial<detail::pure_op<F>> pure{};
97
100
103
106
109 } // namespace cpo
110
112 namespace applicatives {
113 namespace detail {
121 struct fmap_op {
122 template <class Fn, class Fa>
123 constexpr auto operator()(Fn&& fn, Fa&& fa) const noexcept(
124 noexcept( mpc::seq_apply(mpc::pure<Fa>(std::forward<Fn>(fn)), std::forward<Fa>(fa))))
125 -> decltype(mpc::seq_apply(mpc::pure<Fa>(std::forward<Fn>(fn)), std::forward<Fa>(fa)))
126 { return mpc::seq_apply(mpc::pure<Fa>(std::forward<Fn>(fn)), std::forward<Fa>(fa)); }
127 };
128
136 struct liftA2_op {
137 template <class Fn, class Fa, class Fb>
138 constexpr auto operator()(Fn&& fn, Fa&& fa, Fb&& fb) const noexcept(
139 noexcept( mpc::seq_apply(mpc::fmap(std::forward<Fn>(fn), std::forward<Fa>(fa)), std::forward<Fb>(fb))))
140 -> decltype(mpc::seq_apply(mpc::fmap(std::forward<Fn>(fn), std::forward<Fa>(fa)), std::forward<Fb>(fb)))
141 { return mpc::seq_apply(mpc::fmap(std::forward<Fn>(fn), std::forward<Fa>(fa)), std::forward<Fb>(fb)); }
142 };
143
152 template <class Fa, class Fb>
153 constexpr auto operator()(Fa&& fa, Fb&& fb) const noexcept(
154 noexcept( mpc::seq_apply(mpc::replace2nd(identity, std::forward<Fa>(fa)), std::forward<Fb>(fb))))
155 -> decltype(mpc::seq_apply(mpc::replace2nd(identity, std::forward<Fa>(fa)), std::forward<Fb>(fb)))
156 { return mpc::seq_apply(mpc::replace2nd(identity, std::forward<Fa>(fa)), std::forward<Fb>(fb)); }
157 };
158 } // namespace detail
159
161 inline constexpr partial<detail::fmap_op> fmap{};
162
170 inline constexpr auto seq_apply = mpc::liftA2 % identity;
171
174
182 inline constexpr auto discard2nd = mpc::liftA2 % constant;
183
191 inline constexpr auto discard1st = mpc::liftA2 % (flip % constant);
192
195 } // namespace applicatives
196
197 // Grobal methods
198
199 namespace detail {
201 template <class Fn, class Fa, class Fb>
202 constexpr auto reversed_liftA(Fb&& fb, Fa&& fa, Fn&& fn) noexcept(
203 noexcept( mpc::liftA2(std::forward<Fn>(fn), std::forward<Fa>(fa), std::forward<Fb>(fb))))
204 -> decltype(mpc::liftA2(std::forward<Fn>(fn), std::forward<Fa>(fa), std::forward<Fb>(fb)))
205 { return mpc::liftA2(std::forward<Fn>(fn), std::forward<Fa>(fa), std::forward<Fb>(fb)); }
206
207 template <class Fz, class... Args>
208 requires (sizeof...(Args) > 2)
209 constexpr auto reversed_liftA(Fz&& fz, Args&&... args) noexcept(
210 noexcept( mpc::seq_apply(reversed_liftA(std::forward<Args>(args)...), std::forward<Fz>(fz))))
211 -> decltype(mpc::seq_apply(reversed_liftA(std::forward<Args>(args)...), std::forward<Fz>(fz)))
212 { return mpc::seq_apply(reversed_liftA(std::forward<Args>(args)...), std::forward<Fz>(fz)); }
214
222 template <std::size_t N, class = make_reversed_index_sequence<N + 1>>
223 struct liftA_op;
224
226 template <std::size_t N, std::size_t... Idx>
227 requires (N > 1)
228 struct liftA_op <N, std::index_sequence<Idx...>> {
229 // WORKAROUND: std::tuple_element (and therefore, std::get) is not SFINAE-friendly in Clang.
230 template <class Bound, class = std::enable_if_t<sizeof...(Idx) == std::tuple_size_v<Bound>>>
231 constexpr auto operator()(Bound&& bound) const noexcept(
232 noexcept( reversed_liftA(std::get<Idx>(std::forward<Bound>(bound))...)))
233 -> decltype(reversed_liftA(std::get<Idx>(std::forward<Bound>(bound))...))
234 { return reversed_liftA(std::get<Idx>(std::forward<Bound>(bound))...); }
235
236 template <class... Args>
237 requires (sizeof...(Args) == N + 1)
238 constexpr auto operator()(Args&&... args) const noexcept(
239 noexcept( this->operator()(std::forward_as_tuple(std::forward<Args>(args)...))))
240 -> decltype(this->operator()(std::forward_as_tuple(std::forward<Args>(args)...)))
241 { return this->operator()(std::forward_as_tuple(std::forward<Args>(args)...)); }
242 };
243 } // namespace detail
244
245 inline namespace cpo {
247 template <std::size_t N>
248 requires (N > 1)
249 inline constexpr partial<detail::liftA_op<N>> liftA{};
250 } // namespace cpo
251} // namespace mpc
252
253// clang-format on
applicative_traits_specialized
Definition: applicative.hpp:22
Requires functor and pure, seq_apply, liftA2, discard2nd and discard1st is valid in applicative_trait...
Definition: applicative.hpp:32
Requires fmap and replace2nd is valid in functor_traits .
Definition: functor.hpp:18
constexpr partial< detail::liftA2_op > liftA2
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
Definition: applicative.hpp:173
constexpr auto discard1st
discard1st :: f a -> f b -> f b
Definition: applicative.hpp:191
constexpr auto seq_apply
seq_apply :: f (a -> b) -> f a -> f b
Definition: applicative.hpp:170
constexpr auto discard2nd
discard2nd :: f a -> f b -> f a
Definition: applicative.hpp:182
constexpr partial< detail::discard1st_opt_op > discard1st_opt
discard1st :: f a -> f b -> f b
Definition: applicative.hpp:194
constexpr partial< detail::fmap_op > fmap
fmap :: (a -> b) -> f a -> f b
Definition: applicative.hpp:161
constexpr partial< detail::liftA2_op > liftA2
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
Definition: applicative.hpp:102
constexpr partial< detail::constant_op > constant
Returns a unary function always returning the first input.
Definition: constant.hpp:21
constexpr partial< detail::liftA_op< N > > liftA
liftA :: Applicative f => (a -> b -> ... -> z) -> f a -> f b -> ... -> f z
Definition: applicative.hpp:249
constexpr partial< detail::discard1st_op > discard1st
discard1st :: f a -> f b -> f b
Definition: applicative.hpp:108
constexpr partial< detail::replace2nd_op > replace2nd
replace2nd :: a -> f b -> f a
Definition: functor.hpp:53
constexpr partial< detail::discard2nd_op > discard2nd
discard2nd :: f a -> f b -> f a
Definition: applicative.hpp:105
constexpr partial< detail::seq_apply_op > seq_apply
seq_apply :: f (a -> b) -> f a -> f b
Definition: applicative.hpp:99
constexpr partial< detail::flip_op > flip
Returns a binary function which flips the first and second argument.
Definition: flip.hpp:32
constexpr partial< std::identity > identity
Identity mapping.
Definition: identity.hpp:11
constexpr partial< detail::pure_op< F > > pure
pure :: a -> f a
Definition: applicative.hpp:96
class Functor f => Applicative f where
Definition: applicative.hpp:18
discard1st :: f a -> f b -> f b
Definition: applicative.hpp:151
fmap :: (a -> b) -> f a -> f b
Definition: applicative.hpp:121
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
Definition: applicative.hpp:136
discard1st :: f a -> f b -> f b
Definition: applicative.hpp:84
discard2nd :: f a -> f b -> f a
Definition: applicative.hpp:72
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
Definition: applicative.hpp:60
liftA :: Applicative f => (a -> b -> ... -> z) -> f a -> f b -> ... -> f z
Definition: applicative.hpp:223
pure :: a -> f a
Definition: applicative.hpp:39
seq_apply :: f (a -> b) -> f a -> f b
Definition: applicative.hpp:51
Implements a perfect-forwarding call wrapper.
Definition: partial.hpp:63