Полезны ли первоклассные продолжения в современных объектно-ориентированных языках программирования?

10

Продолжения чрезвычайно полезны в функциональных языках программирования (например, в Contмонаде в Haskell), поскольку они допускают простую и регулярную запись кода императивного стиля. Они также полезны в некоторых старых императивных языках, потому что их можно использовать для реализации отсутствующих языковых функций (например, исключений, сопрограмм, зеленых потоков). Но для современного объектно-ориентированного языка со встроенной поддержкой этих функций, какие аргументы будут для добавления поддержки первоклассных продолжений (будь то более современный стиль с разделителями resetи / shiftили схема call-with-current-continuation)?

Есть ли аргументы против добавления поддержки, кроме производительности и сложности реализации?

Жюль
источник

Ответы:

15

Позвольте Эрику Липперту ответить на этот вопрос:

Очевидный вопрос на данный момент: если CPS такой классный, то почему бы нам не использовать его постоянно? Почему большинство профессиональных разработчиков никогда не слышали об этом, или те, кто имеет, думают об этом как о чем-то, что делают только эти сумасшедшие программисты на Scheme?

Во-первых, большинству людей, привыкших думать о подпрограммах, циклах, try-catch-finally и т. Д., Просто сложно рассуждать о том, как делегаты используются для управления потоком таким образом. Я сейчас просматриваю свои заметки о CPS из CS442 и вижу, что в 1995 году я записал профквот: «С продолжениями вы должны встать на голову и вытащить себя наизнанку». Профессор Дагган (*) был абсолютно прав, говоря это. Вспомните пару дней назад, что наш крошечный пример преобразования CPS выражения C # M (B ()? C (): D ()) включал четыре лямбды. Не все хорошо умеют читать код, который использует функции высшего порядка. Это накладывает большие когнитивные нагрузки.

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

В следующей статье он объясняет, как асинхронность и продолжения в точности эквивалентны друг другу, и рассказывает о том, как выполнить простую (но блокирующую) синхронную сетевую операцию и переписать ее в асинхронном стиле, убедившись, что все скрытые есть ошибки, которые должны быть покрыты, чтобы понять это правильно. Это превращается в огромный беспорядок кода. Его резюме в конце:

Боже мой, какой ужасный беспорядок мы сделали. Мы расширили две строки совершенно чистого кода до двух десятков строк самых чудовищных спагетти, которые вы когда-либо видели. И, конечно, он все еще не компилируется, потому что метки не находятся в области видимости, и у нас есть определенная ошибка присваивания. Нам все еще нужно переписать код, чтобы исправить эти проблемы.

Помните, что я говорил о плюсах и минусах CPS?

  • PRO: Произвольно сложные и интересные потоки управления могут быть построены из простых частей - проверьте.
  • CON: Реализацию потока управления через продолжения трудно читать и трудно обдумать - проверить.
  • CON: код, представляющий механизмы потока управления, полностью переопределяет смысл кода - проверка.
  • CON: Преобразование обычного потока управления кодом в CPS - это то, с чем хорошо справляются компиляторы, и почти никто другой - проверка.

Это не какое-то интеллектуальное упражнение. Реальные люди заканчивают тем, что пишут код, морально эквивалентный вышеупомянутому, все время, когда они имеют дело с асинхронностью. И по иронии судьбы, даже несмотря на то, что процессоры стали быстрее и дешевле, мы проводим все больше и больше времени в ожидании того, что не связано с процессором. Во многих программах большая часть времени тратится на привязку процессора к нулю в ожидании сетевых пакетов, чтобы совершить поездку из Англии в Японию и обратно, или вращения дисков, или чего-то еще.

И я даже не говорил о том, что произойдет, если вы захотите составить асинхронные операции дальше. Предположим, вы хотите сделать ArchiveDocuments асинхронной операцией, которая принимает делегата. Теперь весь код, который его вызывает, также должен быть написан на CPS. Загрязнение просто распространяется.

Правильная асинхронная логика важна, она станет еще более важной в будущем, а инструменты, которые мы вам дали, заставят вас «стоять на голове и вывернуть себя наизнанку», как мудро сказал профессор Дагган.

Стиль передачи продолжения является мощным, да, но должен быть лучший способ эффективно использовать эту мощь, чем код выше.

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

Мейсон Уилер
источник
5
Стоит отметить, что если ваш язык поддерживает такие синтаксические расширения, как макросы, то продолжения становятся гораздо более полезными, поскольку вы можете скрыть механизмы реализации различных интересных потоков управления внутри расширений синтаксиса. По сути, именно так и работает «await», это просто определено в языке, поскольку C # не предоставляет инструментов для самостоятельного определения таких синтаксических расширений.
Джек
Хорошая статья, хотя я отмечу, что CPS и продолжения первого класса - это не одно и то же (CPS - это то, что вы делаете на языке без поддержки продолжений, когда вы обнаружите, что они вам нужны). Похоже, что C # идет по тому же маршруту, о котором я думаю (хотя я не могу решить, хочу ли я автоматизировать преобразование шин в CPS или реализовать полномасштабные продолжения с копированием в стеке).
Жюль
@Jack - это именно то, что я сейчас рассматриваю: язык, который я разрабатываю, имеет макро-функцию, которая может автоматически поддерживать преобразование CPS. Но полные нативные продолжения могут дать лучшую производительность ... вопрос в том, как часто они будут использоваться в критической ситуации?
Жюль