Не очень понятен такой момент, в первом примере со State, у нас >= возвращал обернутое в состояние значение, а > был finally - возвращал просто значение. В примере со списками же flatmap берет функцию, строящую из элемента список и применяет ее к списку, но в отличие от fmap, возвращает не список списков, а просто список. Чем не уже готовый finally? Почему finally это просто fmap, которой наоборот, если дать функцию из элемента списка в список и сам список, построит нечто обернутое - список списков, а не flatmap?
Обоснование может быть в том, что >>= правоассоциативна, поэтому если ее перегружать, то придется раставлять скобочки: ((wrap_state(make_pair(0, 0)) >>= nfoo) >>= nbar) >> buz;
Что касается аттрибута pure... Идея замечательная, но я считаю, что это ни в коем случае не должен быть аттрибут. Будет некрасиво, если аттрибут будет менять наблюдаемое поведение программы. А этот аттрибут либо будет, поскольку побочные эффекты вполне наблюдаемы, либо будет крайне ограничен. Если мы не хотим менять наблюдаемое поведение - компилятору придётся в случае отсутствия тела пессимистично предполагать, что функция не чистая, чтобы не испортить корректность кода Вероятно это должен быть оператор + спецификатор, являющийся частью типа функции. Тогда, во-первых, мы сможем обязать компилятор проверять, что функция действительно чистая, а во-вторых, мы сможем делать идеальные врапперы, которые будут повторять чистоту функций 29:44. Раз уж никто не предоставил в комментариях реализацию flatten - буду первым: template auto concat(LIST1 l1, LIST2 l2) { return l1([l2](auto... l1xs){ return l2([l1xs...](auto... l2xs){ return List(l1xs..., l2xs...); }); }); }; template auto concat(LIST1 l1, LIST2 l2, REST... rest) { return concat(concat(l1, l2), rest...); }; auto flatten = [](auto f, auto... xs) { return concat(List(), List(), f(xs)...); }; Но решение определённо занимает больше 4 строк. Применив н̶е̶м̶н̶о̶г̶о̶ ̶м̶а̶г̶и̶и̶ fold expressions и C++20, можно заметно сократить пример: // godbolt.org/z/9b8z1a auto operator+(auto list1, auto list2) { return list1([list2](auto... l1xs){ return list2([l1xs...](auto... l2xs){ return List(l1xs..., l2xs...); }); }); }; auto flatten = [](auto f, auto... xs) { return (f(xs) + ...); }; Ну... тела действительно теперь в сумме занимают 4 строчки, но... То ли требовалось от решения или здесь есть более лаконичная альтернатива? И да, несмотря на то, что это самая короткая лекция на канале, и я имею при этом небольшое знакомство с ФП, это была, всё же, самая длинная лекция, спасибо) UPD: а̶ ̶д̶а̶в̶а̶й̶т̶е̶ ̶п̶о̶с̶о̶р̶е̶в̶н̶у̶е̶м̶с̶я̶ ̶в̶ ̶о̶б̶ф̶у̶с̶к̶а̶ц̶и̶и̶ ̶к̶о̶д̶а̶ ̶б̶л̶а̶г̶о̶д̶а̶р̶я̶ ̶р̶е̶к̶у̶р̶с̶и̶в̶н̶ы̶м̶ ̶л̶я̶м̶б̶д̶а̶м̶? constexpr auto flatten = [](auto func, auto x1, auto x2, auto... rest) { if constexpr (sizeof...(rest)) return operator()(func, operator()(func, func(x1), x2), rest...); else return x1([func, x2](auto... l1xs){ return func(x2)([l1xs...](auto... l2xs){ return List(l1xs..., l2xs...); }); }); }; Этот код работает в GCC и MSVC, Clang же не допускает использование operator() в теле лямбды ( godbolt.org/z/4rK5ra ), думаю, здесь стоит поискать ответ в стандарте, кто же прав
Поскольку UA-cam выдал shadow ban на мой же ответ под этим сообщением, я, спустя 4 месяца, и во второй раз просто его скопирую, удалив ссылку на стандарт (не позволяет даже часть ссылки оставить) и наше C++ сообщество в Телеграм, где дали эти пояснения: Кажется, Clang прав: The lambda-expression's compound-statement yields the function-body ([dcl.fct.def]) of the function call operator, but for purposes of name lookup, determining the type and value of this and transforming id-expressions referring to non-static class members into class member access expressions using (*this) ([class.mfct.non-static]), t͟h͟e͟ ͟c͟o͟m͟p͟o͟u͟n͟d͟-͟s͟t͟a͟t͟e͟m͟e͟n͟t͟ ͟i͟s͟ ͟c͟o͟n͟s͟i͟d͟e͟r͟e͟d͟ ͟i͟n͟ ͟t͟h͟e͟ ͟c͟o͟n͟t͟e͟x͟t͟ ͟o͟f͟ ͟t͟h͟e͟ ͟l͟a͟m͟b͟d͟a͟-͟e͟x͟p͟r͟e͟s͟s͟i͟o͟n А in the context of the lambda-expression нам недоступен operator(), однако, это могло бы быть очень приятным синтаксическим сахаром
В FORTRAN есть pure спецификатор 😃
Не очень понятен такой момент, в первом примере со State, у нас >= возвращал обернутое в состояние значение, а > был finally - возвращал просто значение. В примере со списками же flatmap берет функцию, строящую из элемента список и применяет ее к списку, но в отличие от fmap, возвращает не список списков, а просто список. Чем не уже готовый finally? Почему finally это просто fmap, которой наоборот, если дать функцию из элемента списка в список и сам список, построит нечто обернутое - список списков, а не flatmap?
А грядущий consteval не является аналогом атрибута [[pure]]? Ну или близким родственником.
Не является. Consteval означает что функции вообще не будет на этапе исполнения. А pure означает чистое исполнение. Немного разные вещи
@@tilir Понятно. Спасибо за пояснение.
Так почему ты переопределил операторы > и >=, а не операторы >> и >>=?
Никаких серьёзных обоснований тут нет. Можно взять любые.
Обоснование может быть в том, что >>= правоассоциативна, поэтому если ее перегружать, то придется раставлять скобочки:
((wrap_state(make_pair(0, 0)) >>= nfoo) >>= nbar) >> buz;
Что касается аттрибута pure... Идея замечательная, но я считаю, что это ни в коем случае не должен быть аттрибут. Будет некрасиво, если аттрибут будет менять наблюдаемое поведение программы. А этот аттрибут либо будет, поскольку побочные эффекты вполне наблюдаемы, либо будет крайне ограничен. Если мы не хотим менять наблюдаемое поведение - компилятору придётся в случае отсутствия тела пессимистично предполагать, что функция не чистая, чтобы не испортить корректность кода
Вероятно это должен быть оператор + спецификатор, являющийся частью типа функции. Тогда, во-первых, мы сможем обязать компилятор проверять, что функция действительно чистая, а во-вторых, мы сможем делать идеальные врапперы, которые будут повторять чистоту функций
29:44. Раз уж никто не предоставил в комментариях реализацию flatten - буду первым:
template
auto concat(LIST1 l1, LIST2 l2) {
return l1([l2](auto... l1xs){
return l2([l1xs...](auto... l2xs){
return List(l1xs..., l2xs...);
});
});
};
template
auto concat(LIST1 l1, LIST2 l2, REST... rest) {
return concat(concat(l1, l2), rest...);
};
auto flatten = [](auto f, auto... xs) {
return concat(List(), List(), f(xs)...);
};
Но решение определённо занимает больше 4 строк. Применив н̶е̶м̶н̶о̶г̶о̶ ̶м̶а̶г̶и̶и̶ fold expressions и C++20, можно заметно сократить пример:
// godbolt.org/z/9b8z1a
auto operator+(auto list1, auto list2) {
return list1([list2](auto... l1xs){
return list2([l1xs...](auto... l2xs){
return List(l1xs..., l2xs...);
});
});
};
auto flatten = [](auto f, auto... xs) {
return (f(xs) + ...);
};
Ну... тела действительно теперь в сумме занимают 4 строчки, но... То ли требовалось от решения или здесь есть более лаконичная альтернатива?
И да, несмотря на то, что это самая короткая лекция на канале, и я имею при этом небольшое знакомство с ФП, это была, всё же, самая длинная лекция, спасибо)
UPD: а̶ ̶д̶а̶в̶а̶й̶т̶е̶ ̶п̶о̶с̶о̶р̶е̶в̶н̶у̶е̶м̶с̶я̶ ̶в̶ ̶о̶б̶ф̶у̶с̶к̶а̶ц̶и̶и̶ ̶к̶о̶д̶а̶ ̶б̶л̶а̶г̶о̶д̶а̶р̶я̶ ̶р̶е̶к̶у̶р̶с̶и̶в̶н̶ы̶м̶ ̶л̶я̶м̶б̶д̶а̶м̶?
constexpr auto flatten = [](auto func, auto x1, auto x2, auto... rest) {
if constexpr (sizeof...(rest)) return operator()(func, operator()(func, func(x1), x2), rest...);
else return x1([func, x2](auto... l1xs){
return func(x2)([l1xs...](auto... l2xs){ return List(l1xs..., l2xs...); });
});
};
Этот код работает в GCC и MSVC, Clang же не допускает использование operator() в теле лямбды ( godbolt.org/z/4rK5ra ), думаю, здесь стоит поискать ответ в стандарте, кто же прав
Поскольку UA-cam выдал shadow ban на мой же ответ под этим сообщением, я, спустя 4 месяца, и во второй раз просто его скопирую, удалив ссылку на стандарт (не позволяет даже часть ссылки оставить) и наше C++ сообщество в Телеграм, где дали эти пояснения:
Кажется, Clang прав:
The lambda-expression's compound-statement yields the function-body ([dcl.fct.def]) of the function call operator, but for purposes of name lookup, determining the type and value of this and transforming id-expressions referring to non-static class members into class member access expressions using (*this) ([class.mfct.non-static]), t͟h͟e͟ ͟c͟o͟m͟p͟o͟u͟n͟d͟-͟s͟t͟a͟t͟e͟m͟e͟n͟t͟ ͟i͟s͟ ͟c͟o͟n͟s͟i͟d͟e͟r͟e͟d͟ ͟i͟n͟ ͟t͟h͟e͟ ͟c͟o͟n͟t͟e͟x͟t͟ ͟o͟f͟ ͟t͟h͟e͟ ͟l͟a͟m͟b͟d͟a͟-͟e͟x͟p͟r͟e͟s͟s͟i͟o͟n
А in the context of the lambda-expression нам недоступен operator(), однако, это могло бы быть очень приятным синтаксическим сахаром