Здравствуйте, Sinclair, Вы писали:
S>Я, наверное, чего-то не понял. Монада задаёт правила комбинирования функций. Поэтому из "одной концепции" получается весь зоопарк, включая M<R>(M<T>) и всякие F(G()).
Не любых функций. Монада задаётся (ну в основном, остальное мелочи) с помощью определения оператора >>= (например) вида "M<R> operator >>= (M<T>, M<R>(T));". Т.е. этот оператор позволяет функции вида M<R>(T) подействовать на M<T> и вернуть M<R>. И всё, точка. То, что путём некоторых манипуляций (тот самый лифтинг) мы можем из этого ещё и суметь подействовать на M<T> функцией вида R(T) и получить M<R>, означает всего лишь констатацию того факта, что любая монада является ещё и функтором. Однако подобное далеко не везде и например в обратную сторону это не верно. Т.е. если мы определим например некоторую сущность F через оператор вида "F<R> operator >>= (F<T>, R(T));", т.е. позволяющему действовать функции R(T) на F<T> и получать F<R> (кстати, именно такой сценарий больше всего наблюдался тут в агитации полезности монад), то из этого у нас уже не получится научиться действовать на F<T> функциями вида F<R>(T), так что монадой это не будет. Но применять функции R(T) к F<T> мы будем спокойно, причём без всякого лифтинга.
Абзац выше — это были как раз классические функциональные игры, хотя и записанные на C++. А вот тот мой пример с Apply — это наоборот был тупо императивный подход. Но при этом он работает не только с функторами или монадами, но и с чем угодно. Хоть с голыми значениями или коллекциями stl. А в сочетание с шаблонной магией C++ это ещё и позволяет писать сложный обобщённый алгоримы, работающий без исправления кода для всех этих разных сущностей одновременно. Причём без капли накладных расходов.
S>Вот, скажем, для Nullable<T> есть встроенные в язык C# правила для лифтинга операторов. А для пользовательских функций "правил лифтинга" нет. И нет никакой возможности описать "правила лифтинга" для своего типа Arbitrary<T> так, чтобы операторы и функции, определённые для T, автоматически конвертировались в операторы и функции, определённые для Arbitrary<T>. Нет средств выразить это в языке. И я никак не вижу способа добиться аналогичного результата в С++.
Я лично пока не понял, а в чём собственно проблема то?
S> Допустим, Петя определил тип optional<T> очевидным образом, предполагая использовать его для
S>value-типов вроде int, double, и так далее.
S>Как мне сделать так, чтобы этот тип корректно работал со всеми, в том числе ещё не написанными, типами?
S>Вот Вася определил тип big_integer.
S>Как мне писать программу с использованием optional<big_integer>?
S>Для big_integer Вася определил множество операторов и функций.
S>Как Пете описать тип optional<T>, чтобы я, прикладной программист, не должен был писать каждый раз
S>S>optional<big_integer> e = a.has_value() ? exp(a.value()) : optional<big_integer>::null;
S>
Ну так а чем функция типа Apply не подходит то? Что-то типа
template<typename R, typename T> auto Apply(optional<T> t, R (*f)(T)) {return t?f(*t):optional<R>();}
Пояснение: в boost'е у optional переопределён оператор bool (возвращает has_value()) и * (возвращает value()) — в таком варианте код записывается короче, но разницы в смысле нет.