Предположим, у меня есть наивная реализация applicative
, то есть имени, которое я выбрал для здравомыслия, но не знаю, что я знаю что-то о классе Applicative
из других языков.Вот реализация:
#include <iostream>
#include <string>
template <typename T>
struct applicative {
template <typename Fn>
auto then(Fn f) const {
return applicative<decltype(f(data_))>{f(data_)};
}
template <typename Fn>
auto and_last(Fn f) const {
return f(data_);
}
T data_;
};
int main() {
applicative<std::string>{"hello world"}
.then([](std::string const& s) {return s.size() * 4; })
.then([](int k) {return k - 2; })
.and_last([](int k) { std::cout << k << "\n"; });
}
Теперь это можно улучшить многими способами.Предоставляя что-то вроде make_applicative
для создания на месте, пытаясь исключить избыточные копии и перемещения, если они есть, и т. Д. Но с тех пор, как я увидел нашу способность создавать функции в C ++, я чувствую, что возможно что-то лучшее.Любая реализация compose будет работать, поэтому давайте выберем одну из них в этом codereview.stackexchange вопросе.С его помощью мы можем сказать что-то вроде
auto f1 = [](std::pair<double,double> p) {return p.first + p.second; };
auto f2 = [](double x) {return std::make_pair(x, x + 1.0); };
auto f3 = [](double x, double y) {return x*y; };
auto g = compose(f1, f2, f3);
std::cout << g(2.0, 3.0) << std::endl; //prints '13', evaluated as (2*3) + ((2*3)+1)
Довольно мило.Возвращаясь к моей идее, я думаю, что это должно сделать возможным переработку моей applicative
реализации следующим образом:
auto sf = applicative<std::string>{}
.then([](std::string const& s) {return s.size() * 4; })
.then([](int k) {return k - 2; });
std::cout << sf.eval_with("hello world"); << "\n";
Как видите, это довольно лениво оценивается, и мы только предоставляем значениекогда нам это нужно, с eval_with
.Я думал о том, как реализовать эту новую версию в течение часа, и я понятия не имею, где хранить операции и составные функции, что делать из параметра шаблона applicative
, как у нас здесь с std::string
, и еще много проблем,Как реализовать что-то подобное?Это тривиально, как я изначально надеялся, или требует много кода?Я действительно хочу этого, потому что я чувствую, что это принесло бы мне много пользы, не позволяя множеству аргументов передавать длинную цепочку функций.
Редактировать: я работаю над этим немного больше и получаю реализацию compose
Я связал не то, что я имел в виду, так как мы все еще выполняем все вызовы функций в цепочке и передаем аргументы.Но вы можете ответить, предполагая, что любая функция compose
будет работать, и выбор лучшей реализации compose
будет оптимизацией производительности.То, что я имел в виду, было больше похоже на следующее, взятое из моего примера
applicative<std::string>{"hello world"}
.then([](std::string const& s) {return s.size() * 4; })
.then([](int k) {return k - 2; })
.and_last([](int k) { std::cout << k << "\n"; });
Эти then
вызовы приведут к одному вызову функции, эквивалентному (s.size() * 4) - 2
, который можно оценить с помощью eval_with
.