-
-
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 - и есть наш список | |
-- этот вариант интереснее тем, что явно виден порядок альтернатив поиска и этот список проще расширять | |
-- Другая интересная штука, что благодаря ленивости если первый поиск удачен, то второй не будет вызван никогда |
`#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); }
};
}
`
Right