-
-
Save drchaos/3e5834472fee96f2331f33a16f1cae53 to your computer and use it in GitHub Desktop.
| #include | |
| #include | |
| #include | |
| #include | |
| #include | |
| #include | |
| #include | |
| /* fmap definitions */ | |
| struct fmap_base | |
| { | |
| struct none_value {}; | |
| template <typename just_t> | |
| struct just | |
| { | |
| just_t m_just; | |
| template<typename... params_t> | |
| just(params_t&&... pp) : m_just{ std::forward<params_t>(pp)... } {} | |
| template<typename unused_param_t> | |
| const just_t& operator()(unused_param_t&&) const { return m_just; } | |
| }; | |
| template <> | |
| struct just<none_value> | |
| { | |
| template<typename... params_t> | |
| just(params_t&&... pp) {} | |
| none_value operator()() const { return none_value{}; } | |
| }; | |
| template <typename res_t, typename intermediate_res_t> | |
| struct fmap_failed | |
| { | |
| fmap_failed(res_t& ret, intermediate_res_t&&) | |
| { | |
| ret = res_t{}; | |
| } | |
| }; | |
| }; | |
| template | |
| struct fmap : public fmap_base | |
| { | |
| private: | |
| template <typename intermediate_res_t, typename fn_t, typename... functors> | |
| struct fmap_ | |
| { | |
| fmap_(res_t& ret, intermediate_res_t&& r, fn_t&& fn, functors&&... ff) | |
| { | |
| if (auto r1 = fn(std::forward<intermediate_res_t>(r))) | |
| { | |
| fmap_<decltype(r1), functors...>(ret, std::move(r1), std::forward<functors>(ff)...); | |
| } | |
| else | |
| { | |
| fmap_failed<res_t, decltype(r1)>( ret, std::move(r1)); | |
| }; | |
| } | |
| }; | |
| template <typename intermediate_res_t, typename fn_t> | |
| struct fmap_<intermediate_res_t, fn_t> | |
| { | |
| fmap_(res_t& ret, intermediate_res_t&& r, fn_t&& fn) | |
| { | |
| ret = fn(std::forward<intermediate_res_t>(r)); | |
| } | |
| }; | |
| public: | |
| template <typename... functors> | |
| fmap(res_t& ret, functors&&...ff) { | |
| fmap_{ ret, | |
| none_value{}, | |
| std::forward<functors>(ff)... }; | |
| } | |
| }; | |
| /* usage definitions, | |
| I use separate types for each stage just for illutrative purposes | |
| */ | |
| struct search_pattern : public std::string | |
| { | |
| using std::string::string; | |
| operator bool() const { return !empty(); } | |
| }; | |
| struct found_param : std::optionalstd::size_t | |
| { | |
| using std::optionalstd::size_t::optional; | |
| // no need to define operator bool() as we borrow it from optional | |
| }; | |
| struct found_value : std::optionalstd::string | |
| { | |
| using std::optionalstd::string::optional; | |
| // no need to define operator bool() as we borrow it from optional | |
| }; | |
| struct param_list { | |
| std::array<std::pair<const char*, const char*>, 1> m_storage{ std::make_pair("good_pattern", "value") }; | |
| }; | |
| found_param find_param(const search_pattern& p, const param_list& params) | |
| { | |
| return params.m_storage[0].first == p ? found_param{ 0 } : found_param{}; | |
| } | |
| found_value find_env_var(const found_param& v, const param_list& params) | |
| { | |
| // we do not attempt to validate if v contains valid index to illustrate it would never be called if p was not found before | |
| return found_value{ params.m_storage[*v].second }; | |
| } | |
| int main() | |
| { | |
| param_list params{}; | |
| found_value good_res{}, bad_res{}; | |
| fmap<found_value>{ good_res, // <- contains "value" at exit | |
| fmap<found_value>::just<search_pattern>("good_pattern") | |
| , [¶ms](const search_pattern& p) -> auto { return find_param(p, params); } | |
| , [¶ms](const found_param& p) -> auto { return find_env_var(p, params); } | |
| }; | |
| fmap<found_value>{ bad_res, // <- contains nothing at exit | |
| fmap<found_value>::just<search_pattern>("bad_pattern") | |
| ,[¶ms](const search_pattern& p) -> auto { return find_param(p, params); } | |
| ,[¶ms](const found_param& p) -> auto { return find_env_var(p, params); } | |
| }; | |
| } |
| -- Вариант с последовательным поиском | |
| lookupParam :: String -> [(String,String)] -> Maybe String | |
| -- реализация | |
| lookupEnvVar :: String -> [(String,String)] -> Maybe String | |
| -- реализация | |
| seqLookup :: String -> [(String,String)] -> [(String,String)] -> Maybe String | |
| seqLookup paramName params envVars = do | |
| envVarName <- lookupParam paramName params | |
| value <- lookupEnvVar envVarName envVars | |
| return value | |
| -- Вариант с альтернативным поиском | |
| altLookup :: String -> [(String,String)] -> [(String,String)] -> Maybe String | |
| altLookup paramName = lookupParam paramName params <|> envVarName paramName params | |
| -- или | |
| altLookup' :: String -> [(String,String)] -> [(String,String)] -> Maybe String | |
| altLookup' paramName = asum [ lookupParam paramName params | |
| , envVarName paramName params | |
| ] | |
| -- тут `asum` это просто свёртка списка с помощью `<|>` | |
| -- `foldr (<|>) empty l`, где empty - это Nothing для нашего случая, а l - и есть наш список | |
| -- этот вариант интереснее тем, что явно виден порядок альтернатив поиска и этот список проще расширять | |
| -- Другая интересная штука, что благодаря ленивости если первый поиск удачен, то второй не будет вызван никогда |
Right
`#include
#include
#include
#include
#include
#include
#include
/* fmap definitions */
struct fmap_base
{
struct none_value {};
template <typename just_t>
struct just
{
just_t m_just;
template<typename... params_t>
just(params_t&&... pp) : m_just{ std::forward<params_t>(pp)... } {}
template<typename unused_param_t>
const just_t& operator()(unused_param_t&&) const { return m_just; }
};
template <>
struct just<none_value>
{
template<typename... params_t>
just(params_t&&... pp) {}
none_value operator()() const { return none_value{}; }
};
template <typename res_t, typename intermediate_res_t>
struct fmap_failed
{
fmap_failed(res_t& ret, intermediate_res_t&&)
{
ret = res_t{};
}
};
};
template
struct fmap : public fmap_base
{
private:
template <typename intermediate_res_t, typename fn_t, typename... functors>
struct fmap_
{
fmap_(res_t& ret, intermediate_res_t&& r, fn_t&& fn, functors&&... ff)
{
if (auto r1 = fn(std::forward<intermediate_res_t>(r)))
{
fmap_<decltype(r1), functors...>(ret, std::move(r1), std::forward<functors>(ff)...);
}
else
{
fmap_failed<res_t, decltype(r1)>( ret, std::move(r1));
};
}
};
template <typename intermediate_res_t, typename fn_t>
struct fmap_<intermediate_res_t, fn_t>
{
fmap_(res_t& ret, intermediate_res_t&& r, fn_t&& fn)
{
ret = fn(std::forward<intermediate_res_t>(r));
}
};
public:
template <typename... functors>
fmap(res_t& ret, functors&&...ff) {
fmap_{ ret,
none_value{},
std::forward<functors>(ff)... };
}
};
/* usage definitions,
I use separate types for each stage just for illutrative purposes
*/
struct search_pattern : public std::string
{
using std::string::string;
operator bool() const { return !empty(); }
};
struct found_param : std::optionalstd::size_t
{
using std::optionalstd::size_t::optional;
// no need to define operator bool() as we borrow it from optional
};
struct found_value : std::optionalstd::string
{
using std::optionalstd::string::optional;
// no need to define operator bool() as we borrow it from optional
};
struct param_list {
std::array<std::pair<const char*, const char*>, 1> m_storage{ std::make_pair("good_pattern", "value") };
};
found_param find_param(const search_pattern& p, const param_list& params)
{
return params.m_storage[0].first == p ? found_param{ 0 } : found_param{};
}
found_value find_env_var(const found_param& v, const param_list& params)
{
// we do not attempt to validate if v contains valid index to illustrate it would never be called if p was not found before
return found_value{ params.m_storage[*v].second };
}
int main()
{
param_list params{};
found_value good_res{}, bad_res{};
fmap<found_value>{ good_res, // <- contains "value" at exit
fmap<found_value>::just<search_pattern>("good_pattern")
, [¶ms](const search_pattern& p) -> auto { return find_param(p, params); }
, [¶ms](const found_param& p) -> auto { return find_env_var(p, params); }
};
fmap<found_value>{ bad_res, // <- contains nothing at exit
fmap<found_value>::just<search_pattern>("bad_pattern")
,[¶ms](const search_pattern& p) -> auto { return find_param(p, params); }
,[¶ms](const found_param& p) -> auto { return find_env_var(p, params); }
};
}
`
Confused.
It is not
altLookup paramName = lookupParam paramName params <|> envVarName paramName paramsbut
altLookup paramName = lookupParam paramName params <|> lookupEnvVar paramName paramsright?