1 /*! 2 @file 3 Defines `boost::hana::monadic_fold_left`. 4 5 @copyright Louis Dionne 2013-2017 6 Distributed under the Boost Software License, Version 1.0. 7 (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) 8 */ 9 10 #ifndef BOOST_HANA_MONADIC_FOLD_LEFT_HPP 11 #define BOOST_HANA_MONADIC_FOLD_LEFT_HPP 12 13 #include <boost/hana/fwd/monadic_fold_left.hpp> 14 15 #include <boost/hana/chain.hpp> 16 #include <boost/hana/concept/foldable.hpp> 17 #include <boost/hana/concept/monad.hpp> 18 #include <boost/hana/config.hpp> 19 #include <boost/hana/core/dispatch.hpp> 20 #include <boost/hana/detail/decay.hpp> 21 #include <boost/hana/fold_right.hpp> 22 #include <boost/hana/functional/curry.hpp> 23 #include <boost/hana/functional/partial.hpp> 24 #include <boost/hana/lift.hpp> 25 26 #include <type_traits> 27 28 29 BOOST_HANA_NAMESPACE_BEGIN 30 //! @cond 31 template <typename M> 32 template <typename Xs, typename State, typename F> operator ()(Xs && xs,State && state,F && f) const33 constexpr decltype(auto) monadic_fold_left_t<M>::operator()(Xs&& xs, State&& state, F&& f) const { 34 using S = typename hana::tag_of<Xs>::type; 35 using MonadicFoldLeft = BOOST_HANA_DISPATCH_IF(monadic_fold_left_impl<S>, 36 hana::Foldable<S>::value 37 ); 38 39 #ifndef BOOST_HANA_CONFIG_DISABLE_CONCEPT_CHECKS 40 static_assert(hana::Monad<M>::value, 41 "hana::monadic_fold_left<M> requires 'M' to be a Monad"); 42 43 static_assert(hana::Foldable<S>::value, 44 "hana::monadic_fold_left<M>(xs, state, f) requires 'xs' to be Foldable"); 45 #endif 46 47 return MonadicFoldLeft::template apply<M>(static_cast<Xs&&>(xs), 48 static_cast<State&&>(state), 49 static_cast<F&&>(f)); 50 } 51 //! @endcond 52 53 //! @cond 54 template <typename M> 55 template <typename Xs, typename F> operator ()(Xs && xs,F && f) const56 constexpr decltype(auto) monadic_fold_left_t<M>::operator()(Xs&& xs, F&& f) const { 57 using S = typename hana::tag_of<Xs>::type; 58 using MonadicFoldLeft = BOOST_HANA_DISPATCH_IF(monadic_fold_left_impl<S>, 59 hana::Foldable<S>::value 60 ); 61 62 #ifndef BOOST_HANA_CONFIG_DISABLE_CONCEPT_CHECKS 63 static_assert(hana::Monad<M>::value, 64 "hana::monadic_fold_left<M> requires 'M' to be a Monad"); 65 66 static_assert(hana::Foldable<S>::value, 67 "hana::monadic_fold_left<M>(xs, f) requires 'xs' to be Foldable"); 68 #endif 69 70 return MonadicFoldLeft::template apply<M>(static_cast<Xs&&>(xs), 71 static_cast<F&&>(f)); 72 } 73 //! @endcond 74 75 namespace detail { 76 struct foldlM_helper { 77 template <typename F, typename X, typename K, typename Z> operator ()detail::foldlM_helper78 constexpr decltype(auto) operator()(F&& f, X&& x, K&& k, Z&& z) const { 79 return hana::chain( 80 static_cast<F&&>(f)( 81 static_cast<Z&&>(z), 82 static_cast<X&&>(x) 83 ), 84 static_cast<K&&>(k) 85 ); 86 } 87 }; 88 89 template <typename End, typename M, typename F> 90 struct monadic_foldl1_helper { 91 F f; 92 template <typename X, typename Y> operator ()detail::monadic_foldl1_helper93 constexpr decltype(auto) operator()(X&& x, Y&& y) const 94 { return f(static_cast<X&&>(x), static_cast<Y&&>(y)); } 95 template <typename Y> operator ()detail::monadic_foldl1_helper96 constexpr decltype(auto) operator()(End, Y&& y) const 97 { return hana::lift<M>(static_cast<Y&&>(y)); } 98 }; 99 } 100 101 template <typename T, bool condition> 102 struct monadic_fold_left_impl<T, when<condition>> : default_ { 103 // with state 104 template <typename M, typename Xs, typename S, typename F> applymonadic_fold_left_impl105 static constexpr decltype(auto) apply(Xs&& xs, S&& s, F&& f) { 106 return hana::fold_right( 107 static_cast<Xs&&>(xs), 108 hana::lift<M>, 109 hana::curry<3>(hana::partial( 110 detail::foldlM_helper{}, static_cast<F&&>(f) 111 )) 112 )(static_cast<S&&>(s)); 113 } 114 115 // without state 116 template <typename M, typename Xs, typename F> applymonadic_fold_left_impl117 static constexpr decltype(auto) apply(Xs&& xs, F&& f) { 118 struct end { }; 119 using G = detail::monadic_foldl1_helper<end, M, typename detail::decay<F>::type>; 120 decltype(auto) result = hana::monadic_fold_left<M>( 121 static_cast<Xs&&>(xs), 122 end{}, 123 G{static_cast<F&&>(f)} 124 ); 125 126 static_assert(!std::is_same< 127 std::remove_reference_t<decltype(result)>, 128 decltype(hana::lift<M>(end{})) 129 >{}, 130 "hana::monadic_fold_left<M>(xs, f) requires 'xs' to be non-empty"); 131 return result; 132 } 133 }; 134 BOOST_HANA_NAMESPACE_END 135 136 #endif // !BOOST_HANA_MONADIC_FOLD_LEFT_HPP 137