Мне нужна помощь в написании читаемого кода для составления монадических обратных вызовов C++23. Я обнаружил, что, хотя код, который я создаю, имеет тенденцию быть более правильным, сценарии, в которых мне нужно объединить результаты двух вычислений, создают код, который значительно менее читаем, чем стандартный способ выполнения задач.
Возьмем пример с дополнительными функциями с ограничением, что foo() должен вызываться перед bar(): в качестве мотивирующего примера предположим, что мы пишем синтаксический анализатор и оба foo и bar потребляют некоторый токен из наш список токенов для анализа.
std::optional foo();
std::optional bar();
И умножить их результаты, если они существуют, или распространить nullopt в противном случае
"Обычный способ" сделать это
std::optional baz() {
std::optional maybe_foo = foo();
if(!maybe_foo.has_value()) {
return std::nullopt;
}
std::optional maybe_bar = bar();
if(!maybe_bar.has_value()) {
return std::nullopt;
}
return maybe_foo.value() * maybe_bar.value();
}
Монадический подход:
std::optional baz() {
return foo().and_then([](int foo_res) {
return bar().and_then([foo_res](int bar_res) {
return foo_res * bar_res;
});
});
}
И эта вложенность меня очень беспокоит. Я обнаружил, что в более сложных вычислениях ситуация становится еще хуже, когда эта растущая пирамида логики вырастает из моих функций, поскольку мы никогда не сможем замкнуть нашу логику.
Что я делаю не так?
В качестве более наглядного примера ниже приведена функция из написанного мною парсера, которую я считаю особенно нечитаемой. Функциональность функции менее важна, чем описанная выше пирамида обратных вызовов...
template
Parser::maybe_expression Parser::assignment() {
// Given an arbitrary expression, return the VariableExpression contained within
// if one exists, otherwise return a nullopt
auto try_extract_variable = [](grammar::Expression expr)
-> std::optional {
return try_get(std::move(expr))
.and_then([](grammar::PrimaryExpression primary_expr) -> std::optional {
return try_get(std::move(primary_expr.data));
});
};
return equality()
.and_then([&](std::unique_ptr expr) {
// If the top token after parsing Equality() is an =, we either return an
// assignment expression or an error. Otherwise, we directly return the Equality() expression
return consume()
.transform([&](const token::Equal &equal) {
// We are parsing an assignment expression, and so we would like to extract the
// Variable that we are to assign, otherwise return an error.
return try_extract_variable(std::move(*expr))
.transform([&](const grammar::VariableExpression &variable) -> maybe_expression {
return expression()
.map([&](std::unique_ptr assign_to) {
return std::make_unique(grammar::AssignmentExpression{
variable, std::move(assign_to), variable.line_number
});
});
})
.value_or(tl::unexpected{Error{equal.line_number,
ErrorType::kBadAssign,
fmt::format("Incomplete assignment expression")
}});
})
.or_else([&] -> std::optional {
return std::move(expr);
})
.value();
});
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... dable-code
Вложенные монадические операции создают нечитаемый код. ⇐ C++
Программы на C++. Форум разработчиков
-
Anonymous
1735930850
Anonymous
Мне нужна помощь в написании читаемого кода для составления монадических обратных вызовов C++23. Я обнаружил, что, хотя код, который я создаю, имеет тенденцию быть более правильным, сценарии, в которых мне нужно объединить результаты двух вычислений, создают код, который значительно менее читаем, чем стандартный способ выполнения задач.
Возьмем пример с дополнительными функциями с ограничением, что foo() должен вызываться перед bar(): в качестве мотивирующего примера предположим, что мы пишем синтаксический анализатор и оба foo и bar потребляют некоторый токен из наш список токенов для анализа.
std::optional foo();
std::optional bar();
И умножить их результаты, если они существуют, или распространить nullopt в противном случае
"Обычный способ" сделать это
std::optional baz() {
std::optional maybe_foo = foo();
if(!maybe_foo.has_value()) {
return std::nullopt;
}
std::optional maybe_bar = bar();
if(!maybe_bar.has_value()) {
return std::nullopt;
}
return maybe_foo.value() * maybe_bar.value();
}
Монадический подход:
std::optional baz() {
return foo().and_then([](int foo_res) {
return bar().and_then([foo_res](int bar_res) {
return foo_res * bar_res;
});
});
}
И эта вложенность меня очень беспокоит. Я обнаружил, что в более сложных вычислениях ситуация становится еще хуже, когда эта растущая пирамида логики вырастает из моих функций, поскольку мы никогда не сможем замкнуть нашу логику.
[b]Что я делаю не так?[/b]
В качестве более наглядного примера ниже приведена функция из написанного мною парсера, которую я считаю особенно нечитаемой. Функциональность функции менее важна, чем описанная выше пирамида обратных вызовов...
template
Parser::maybe_expression Parser::assignment() {
// Given an arbitrary expression, return the VariableExpression contained within
// if one exists, otherwise return a nullopt
auto try_extract_variable = [](grammar::Expression expr)
-> std::optional {
return try_get(std::move(expr))
.and_then([](grammar::PrimaryExpression primary_expr) -> std::optional {
return try_get(std::move(primary_expr.data));
});
};
return equality()
.and_then([&](std::unique_ptr expr) {
// If the top token after parsing Equality() is an =, we either return an
// assignment expression or an error. Otherwise, we directly return the Equality() expression
return consume()
.transform([&](const token::Equal &equal) {
// We are parsing an assignment expression, and so we would like to extract the
// Variable that we are to assign, otherwise return an error.
return try_extract_variable(std::move(*expr))
.transform([&](const grammar::VariableExpression &variable) -> maybe_expression {
return expression()
.map([&](std::unique_ptr assign_to) {
return std::make_unique(grammar::AssignmentExpression{
variable, std::move(assign_to), variable.line_number
});
});
})
.value_or(tl::unexpected{Error{equal.line_number,
ErrorType::kBadAssign,
fmt::format("Incomplete assignment expression")
}});
})
.or_else([&] -> std::optional {
return std::move(expr);
})
.value();
});
}
Подробнее здесь: [url]https://stackoverflow.com/questions/79314226/nested-monadic-operations-produces-unreadable-code[/url]
Ответить
1 сообщение
• Страница 1 из 1
Перейти
- Кемерово-IT
- ↳ Javascript
- ↳ C#
- ↳ JAVA
- ↳ Elasticsearch aggregation
- ↳ Python
- ↳ Php
- ↳ Android
- ↳ Html
- ↳ Jquery
- ↳ C++
- ↳ IOS
- ↳ CSS
- ↳ Excel
- ↳ Linux
- ↳ Apache
- ↳ MySql
- Детский мир
- Для души
- ↳ Музыкальные инструменты даром
- ↳ Печатная продукция даром
- Внешняя красота и здоровье
- ↳ Одежда и обувь для взрослых даром
- ↳ Товары для здоровья
- ↳ Физкультура и спорт
- Техника - даром!
- ↳ Автомобилистам
- ↳ Компьютерная техника
- ↳ Плиты: газовые и электрические
- ↳ Холодильники
- ↳ Стиральные машины
- ↳ Телевизоры
- ↳ Телефоны, смартфоны, плашеты
- ↳ Швейные машинки
- ↳ Прочая электроника и техника
- ↳ Фототехника
- Ремонт и интерьер
- ↳ Стройматериалы, инструмент
- ↳ Мебель и предметы интерьера даром
- ↳ Cантехника
- Другие темы
- ↳ Разное даром
- ↳ Давай меняться!
- ↳ Отдам\возьму за копеечку
- ↳ Работа и подработка в Кемерове
- ↳ Давай с тобой поговорим...
Мобильная версия