C++ lectures at MIPT (in Russian). Lecture 9a. Monads

preview_player
Показать описание
Лекции в магистратуре МФТИ по C++ на русском языке.

Это маленькое дополнение к лекции 8, которое очень важно само по себе и для будущего изложения.

Рассматриваются чистые вычисления, упорядочение побочных эффектов, концепция монады и виды монад. Разбирается монада List.

Лектор: Константин Владимиров
Дата лекции: 6 декабря 2019 года
Съёмка, звук и единственный слушатель в аудитории: Дмитрий Рябцев

Errata:
* пока здесь пусто
Рекомендации по теме
Комментарии
Автор

Не очень понятен такой момент, в первом примере со State, у нас >= возвращал обернутое в состояние значение, а > был finally - возвращал просто значение. В примере со списками же flatmap берет функцию, строящую из элемента список и применяет ее к списку, но в отличие от fmap, возвращает не список списков, а просто список. Чем не уже готовый finally? Почему finally это просто fmap, которой наоборот, если дать функцию из элемента списка в список и сам список, построит нечто обернутое - список списков, а не flatmap?

samolisov
Автор

А грядущий consteval не является аналогом атрибута [[pure]]? Ну или близким родственником.

yogthemuskrat
Автор

Так почему ты переопределил операторы > и >=, а не операторы >> и >>=?

dimhazel
Автор

Что касается аттрибута pure... Идея замечательная, но я считаю, что это ни в коем случае не должен быть аттрибут. Будет некрасиво, если аттрибут будет менять наблюдаемое поведение программы. А этот аттрибут либо будет, поскольку побочные эффекты вполне наблюдаемы, либо будет крайне ограничен. Если мы не хотим менять наблюдаемое поведение - компилятору придётся в случае отсутствия тела пессимистично предполагать, что функция не чистая, чтобы не испортить корректность кода

Вероятно это должен быть оператор + спецификатор, являющийся частью типа функции. Тогда, во-первых, мы сможем обязать компилятор проверять, что функция действительно чистая, а во-вторых, мы сможем делать идеальные врапперы, которые будут повторять чистоту функций

29:44. Раз уж никто не предоставил в комментариях реализацию flatten - буду первым:

template<typename LIST1, typename LIST2>
auto concat(LIST1 l1, LIST2 l2) {
return l1([l2](auto... l1xs){
return l2([l1xs...](auto... l2xs){
return List(l1xs..., l2xs...);
});
});
};

template<typename LIST1, typename LIST2, typename REST>
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 ), думаю, здесь стоит поискать ответ в стандарте, кто же прав

oficsu