Pull to refresh

Comments 70

SwitchContext(&loop_context_, ¤t_fiber_->context_);
// 0
if (current_fiber_->future_) {
// 1
    current_fiber_->future_.Subscribe(
    [this, fiber = current_fiber_] {
         fiber->future_ = nullptr;
         run_queue_.push_back(fiber);
    });
// 2
} else {
// 3
}


Не очень понял что происходит в этом месте.
Пусть мы поставили задачу в очередь.
Теперь мы ее извлекли и переключили контекст на эту задачу (в точке 0).
Так как мы это сделали первый раз, то идем в точку 1 и подписываемся на событие (пока не будем обращать внимание, что произойдет дальше). Когда событие произойдет, то мы добавим его снова в очередь, и когда-нибудь извлечем его и попадем в точку 0. Из нее мы перейдем в точку 3. Что дальше?
Как я понимаю, дальше мы вернемся в метод WaitFor и продолжим выполнение дальше, так как мы загрузили этот контекст. Но почему этого же не происходит в точке 2?

В точке 2 нет никакого возврата/переключения контекста.
Вы попадаете в точку 0 из задачи (из её WaitFor), задача установила future, поэтому работает ветка 1. Но эта ветка ничего не кладёт в очередь, а всего лишь подписывается на событие. И больше контекста задачи нет нигде в очереди; обратно вы никак не попадёте, пока не сработает событие и не пнёт задачу снова в очередь. А тогда переключение перед точкой 0 перейдёт обратно в задачу и всё. Обратно после WaitFor задача уже не переключается, ни в какую точку в цикле вы уже оттуда не попадёте.

Внимательнее пересмотрел слайды и вроде понял. Но пока не понял еще один момент: когда выполняется основной цикл for в Loop? Как я понимаю, после первой итерации он уходит в «задачу», и из задачи управление возвращается только при WaitFor. Но если никто не вызывает WaitFor, то как продолжится выполнение цикла?

В статье пропущен метод создания корутины.
В него передаётся коллбек на наш код.
В основном метод создания корутины делает всего две вещи:
Вызывает коллбек,
Переключение контекста в Loop.

Немного не дописал свой комментарий. Имел в виду, что будет, если задачи которые выполнялись, завершились, а что-то еще осталось в очереди
То есть как я понимаю, выполнение цикла в Loop продолжается если только ктото вызывает SwitchContext. А если никто не вызывает, то и выполнения не будет. Или я чего-то не понял…

А еще проверки на пустую очередь нету

Конкретно этот цикл (в этом потоке процессора) — никак. Поток будет исполнять рабочую задачу. Оно в целом получается так, что саму задачу вы начинаете стартуя новый поток (или кладя в очередь для пула потоков) — и потом она так и движется, где-то переключаясь в ожидание, где-то возвращаясь в себя, пока не выполнится. А в самом конце она либо вернёт руль пулу потоков, либо завершится.


Ну и проверки на пустую очередь нет на слайде, но она, очевидно, есть в реальном коде. Скорее всего ждёт нового задания в очереди (либо через событие, либо вот так же через future).


А в целом, если рассматривать переключение контекста именно как такой хитрый goto, весь подход — это просто очень хорошо оформленный спагетти-код. Вся логика приложения в одном месте, всё ожидание — в другом, а поток прыгает туда-сюда и по сути с его точки зрения эта самая "лапша" и получается. И главный ключевой момент — как раз переключение контекста. Не многочисленные вызовы функций, которые оставляют кучу своего наследства на стеке, а множество стеков и такой замаскированный goto

Удивлялся этой тенденции. Традиционно в тех языках где была «из коробки» многопоточность, ее обозначали как навык выше среднего. Сейчас ее пихают в самые неподходящие места причем с упорством достойного лучшего применения. На практике же добавляется пачка трудно отлавливаемых багов, особенно в мобильных приложениях или в любых других где происходит взаимодействие с UI. Возможно стоит перейти к модели fork? Она хоть и даст накладные расходы, но позволит писать более понятные и прямолинейные приложения. И на крайний случай использовать шину событий, тут конечно есть свои подводные камни, но код станет немного проще.
Сопрограммы позволяют решить проблемы и задачи там, где нужна производительность и простота кода. Многопоточность нужна для эффективности. Если эта эффективность и производительность не нужна, то тогда и остальное не нужно, включая сопрограммы.

А какой вариант предлагается вместо этого? Модель fork — можно пояснить, что это за модель?
Спасибо за ответ. Но тогда скажите, пожалуйста, зачем удваивать слова: 1 слово на английском — coroutines vs 2 слова на русском — корутины и сопрограммы? ИМХО такое удвоение несет доп. трудности не только пониманию, но, даже, тривиальному поиску по ключевым словам.
Многие привыкли называть их «корутинами», не разбираясь, как переводится. Вот поэтому можно встретить «корутины». А «сопрограммы» — правильный перевод слова «coroutines».
Привычка — «хороший» довод. Кто-то еще привык называть «копрограммы», а кто-то «сорутины». Уже 4 варианта! Скоро в рамках только Хабра ничего найти будет невозможно.
UFO just landed and posted this here
Тут уж нет, «копрограммами» их может назвать только тот, кто никогда не сталкивался с настоящей.
Предполагаю, что многие IT спецы не сталкивались. Но это не важно — минутный взгляд в вики, и автор обсуждаемой статьи выбрал бы грамотный термин, как справедливо отмечено выше:
«сопрограммы» — правильный перевод слова «coroutines»
Всё дело в сложности перевода. Так, некоторые переводчики видят связь термина Coroutine с Subroutine, который часто переводят как «Подпрограмма» (кстати, в англ. языке есть термин Subprogram). Эти переводчики видят, что Routine переводится как «программа» и, соответственно, переводят как «Сопрограмма».

Другие переводчики видят, что в русском языке уже есть слово «рутина», а значит можно перевести как «корутина» (приставка ко- в данном случае означает тоже, что и приставка со-: общее участие, общая принадлежность).

Для Cooperative Multitasking (он же Non-Preemptive Multitasking) тоже два варианта перевода: совместная и кооперативная мультизадачность.

Ну и, если вам обидно за путанницу в русском языке, которая якобы отсутствует в английском, напомню, что у Subroutine есть синонимы в виде Subprogram, Routine, Procedure, Function, Method, так что путаница возникает не только в русском языке.
Другие переводчики видят, что в русском языке уже есть слово «рутина»
В русском это слово не имеет отношения к терминам программирования. Как и другое созвучное слово, про которое справедливо сказали выше:
«Копрограмма» — это совсем из другой области. :)

Показательно, что в вики нет переадресации «корутина — сопрограмма», т.е. согласно вики слова «корутина» нет.
Procedure, Function, Method
— имеют гораздо более узкое, четко определенное значение. Функция возвращает результат, а процедура нет, а метод это процедура или функция какого-либо класса (во всяком случае, так в ОО Паскале). Так что нет в английском путаницы, как и в грамотном русском-техническом. Нет никакой сложности перевода — термин Coroutine давно переведен и отражен в рувики.
если вам обидно за путанницу
Мне «до лампочки фиолетово» и совершенно не обидно, что в какой-то публикации проявилась элементарная безграмотность. Дело не в несуществующей обиде, а в том, что формат Хабра предполагает обсуждение статьи в виде комментов. В этих обсуждениях читатели зачастую высказывают пожелания по улучшению статьи, устранению в ней ошибок. Я, как и многие, указываю в комментах замеченные мной ошибки, и обычно авторы благодарят за желание помочь, хотя (естественно) не всегда соглашаются. Здесь, в отличие от многих других авторов Хабра, автор даже не ответил на мое замечание, ну что же — его право считать свою статью выше всякой критики — написал бы это в статье в явном виде: «статья идеальная, поэтому критика не принимается» — я бы тогда и не стал лезть со своим советом.

Несколько странно видеть такое количество молчаливо несогласных читателей с этим моим советом. Видимо, они за путаницу в терминологии, за искусственное усложнение языка, а некоторые даже за пароли типа QWERTY1234. Ok — каждый вправе выбирать для себя заблуждения без обсуждения и учета каких-либо мнений. Не надо только потом жаловаться, что уровень публикаций на Хабре падает.
Я ни разу в жизни не слышал в бытовой речи слово «сопрограмма». Все говорят «корутины» или «файберы». С чего бы в расшифровке доклада писать слово, которое на практике никто не употребляет?

Язык — подвижная штука, пресловутое кофе, которое когда-то было мужского рода, все знают. Кофе существовало веками, а корутины многие люди открыли только вчера. Зачем тащить легаси слово «сопрограммы»? Если бы его кто-то использовал и оно было необходимо для понимания — может быть. Но его никто не использует. То есть можно выбрать, какое из слов использовать. Я выбираю «корутины», потому что это глобальная международная терминология, которую поймут где угодно в мире, одно-единственное слово на все популярные языки.
Я ни разу в жизни не слышал в бытовой речи слово «сопрограмма».
Как интересно: а я ни разу не слышал слово «корутины», все говорят «сопрограмма», если говорят на русском. И авторы рувики говорят «сопрограмма». Термин укоренился со времен СССР см., нпр., руководство к процессору Электроника 60. И речь не о бытовой речи — в быту я могу сказать, что "в кейсе как попка клаву топтать кончишь фейсом об тэйбл", но согласитесь, такое высказывание явно не для статьи.
Олег, а я и не знал, что Вы столь юны…
> Я выбираю «корутины», потому что это глобальная международная терминология

Терминология может быть международной, но это слово вполне себе переводится. Корутины — так, конечно, можно говорить, но это жаргонизм, примерно как «шедулить» вместо «планировать», «заводить баги» вместо «создавать задачи/дефекты». Т.е. понять могут, но по-русски это несколько безграмотно.

Вы почти написали async/await из TPL. Вообще, конечно, аналогии с шарпом не помешали бы. Его многие знают :)

Вообще еще явные аналогии с futures-rs видны, хотя в последнем это как-то изящнее смотрится за счет АТД.

Мне кажется, те кто знают C# и его async/await — явно не целевая аудитория доклада…

Интересно, где бы был nginx, как платформа, если б не гнался за копейками и внедрил, скажу тоосто, сразу Rust. Я еще помню, как больно было лет пять назад писать модули для nginx без этих вот абстракций на голом Си (вздыхает). Неужели это правда необходимо, как пишет автор статьи?

Его бы не было.
Он как раз примечателен тем, что один из первых решил для массового пользователя задачу 10К, причём весьма изящно и эффективно, ещё в те времена, когда хрестоматийного метода решения подобных задач ещё не было. И заодно допилил средства, чтобы подобные задачи было решать удобно. Игорь Сысоев же не только сам nginx написал, но ещё и к частям ОС, которые были в том состоянии для решения этой задачи неудобны руку приложил. Нагуглите про разницу между epoll и kqueue, например. До сих пор одним из самых исчерпывающих и понятных материалов находится ответ Игоря на эту тему в старой почтовой эхе или на форуме.
И тем самым nginx стал референсом. Когда такая задача возникала где-то ещё — говорили про nginx и смотрели, как сделано там. Уверен, что и разработчики rust тоже смотрели на него же.
И, кстати, ни за какими копейками он в то время не гнался. Это уже современная модель, она позже появилась.

и внедрил, скажу тоосто, сразу Rust.

И как, по вашему, должен выглядеть этот перешедший на rust nginx? Что конкретно вы хотите поменять и на что.
после строчек read и white поток выполнения


Здесь, видимо, опечатка: white -> write
Можете подсказать, как писать, если есть вероятность, что «будущее» не наступит? Т.е. вот мы пишем запрос в базу, а у нас кабель обрубили. Как написать, чтобы по тайм-ауту мы переходили к освобождению или переинициализации ресурсов?
«Будущее» всегда наступает, просто иногда с негативным результатом. Обрабатывается негативный результат точно так же, как и в обычном синхронном коде — проверки флагов, монады optional или either, исключения…
еще просятся таймеры, для отлавливания timeouts — типа «мы послали запрос, но ответ так и не пришел N секунд»
В случае, когда мы писали синхронный код, мы скрыли вопрос полностью под капот и сказали, что этим будет заниматься операционная система, разрешили ей прерывать и перепланировать наш потоки выполнения.

Хорошо.

В язык программирования нужно добавить конструкции, позволяющие выражать темпоральные связи между событиями — в текущем моменте времени и в неопределенном моменте в будущем.

И бам, фундаментальная ошибка в логике. И виной тому размытые понятия. С(и С++) не относится к тем языках, которые понимаются под языками выше. С++ может управлять потоком выполнения и никакой ОС для этого ему ненужна.

Такие же ошибки существуют и выше, когда так же путаются понятия. select/epoll — это синхронные api, никакая асинхронность никакой эффективности не даёт. epoll + неблокирующие сокеты — это именно взлом абстракции, неэффективной абстракции(sockets api). Мы имеем на уровне сети один поток, на уровне ОС один поток, на уровне сокетов много. Это проблема.

Мультиплексинг занимается именно объединением этого «много» опять в один поток. По-сути мы попросту выкидываем.

nginx же написан подобным образом не потому, что ему нужна какая-то асинхронность на уровне io(ведь мультиплексер(epoll) делает io опять СИНХРОННЫМ). Все эти события/реакции — это именно внутренняя архитектура nginx, а io в неё попросту интегрировано.

Но это Scala, а что делать нам, С++ разработчикам?

Ничего — скала не может управлять потоком исполнения. С++ может. Все эти абстракции тут попросту ненужны.

6. Как сделать код читаемее с помощью корутин

И только с этого шага говорится о чём-то актуальном для С++.

Тогда мы реформируем код таким образом:

Код не очень хорош. Контекст передаётся неявно, а значит все эти WaitFor попросту нагромождения ненужных абстракций.

6.1. Корутины

И именно это трувей.

boost::fiber

А этот код уже хорош. Там нет(т.к. ненужно) ничего из того, что описывалось выше.

Чтобы доработать конструкцию до рабочего состояния, нужно как-то оперировать запланированными задачами. Обычно планируемую задачу называют Fiber — логический поток выполнения. Мы её будем представлять как пару контекст+Future. Для того, чтобы привязывать все конструкции с ожиданием, будем хранить в каждом логическом потоке выполнения ту Future, которую он сейчас ждет.

Автор всё не унимается и пытается натянуть неудачные(в контексте С++) абстракции на С++. Зачем, если автор знает про boost::fiber?

Мы сможем написать код, который выглядит как синхронный, в нем явно размечены точки ожидания, они хорошо читаются.

Это причина всего этого нагромождения неактуальных вещей? Ну дак это глупость, ведь смысл синхронного кода в том, что он выглядит так, будто-бы в нём нету ожидания. Зачем это выделять? Какой в этом смысл?

Можно ли сделать лучше?

Да. boost::fiber. В С++ можно реализовать на уровне языка прозрачное блокирующие api и именно это сделано в boost::fiber. Зачем и для чего нужна вся эта борьба с мельницами — неясно.

Когда будет Coroutine TS, можно будет убрать скобочки в коде и сказать, что WaitFor и CoroutineWait, который получается из CoroutineTS — это более-менее одинаковые сущности.

Нет.

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

Нет.

Нужно понимать, что «CoroutineTS» это именно код, находящий в контексте «нельзя прерывать поток выполнения», т.е. всё возвращается к тому, что С++ становится скалой/жаваскриптом. И именно поэтому там такой api, а не потому что это попытка сделать лучше(то, что показано выше. Что показано выше лучше не сделать и так делать вообще ненужно).

К тому же, семантика «CoroutineTS» намного глубже и он полностью изменяет семантику функций. От того он и заменяет базовые/добавляет новые ключевые слова. С показанными выше изваяниям это так же никак не связано.

Именно поэтому все сетевые вещи типа веб-серверов строятся как асинхронный код,

Да не строят их как какой-то асинхронный код. Это обычная логика. Никто нигде и не обязан отделять какие-то там отдельные соединения в отдельные же потоки. Это всё накрученные сверху абстракции.

Если у вас не очень нагруженное приложение, и вам достаточно этого уровня, то дальше идти не нужно, вы можете здесь остаться, и все будет хорошо.

Там всё будет хорошо в любом случае. Существуют единицы приложений, которые действительно от этого пострадают, особенно в контексте сравнения с жаваскриптов/скалой.

Если вы автор nginx, у вас, к сожалению, тернистый путь, вам нужно явно работать с низкими уровнями, вписать этот сложный код. И если вы хотите максимально уменьшать накладные расходы, то вы срезаете все абстракции, в частности, не используете никаких future и promises.

Опять же — размывание понятий. Всякие «future и promises» не используются не потому, что нужно срезать какие-то абстракции — просто эти абстракции из совершенно другого мира и подхода.

Тот же coroTS никак тому же nginx не помешает, как и любые другие адекватные подходу абстракции.

Тогда вам будут удобны абстракции типа futures, promises и actors. Это может сэкономить время. При этом эти абстракции можно более глубоко интегрировать в язык за счет сопрограмм, как я постарался проиллюстрировать.

Зачем? Есть потоки. Не хватает потоков — есть юзерспейс потоки с той же семантикой. Никакого лишнего мусора ненужно — бери boost::fiber и пользуйся.

Какие-то заморочки нужны в случае предыдущем.

Что-то вы сами себе противоречите...


Опять же — размывание понятий. Всякие «future и promises» не используются не потому, что нужно срезать какие-то абстракции — просто эти абстракции из совершенно другого мира и подхода.

Тот же coroTS никак тому же nginx не помешает, как и любые другие адекватные подходу абстракции.

А ничего, что сопрограммы основаны на «future и promises»?

> А ничего, что сопрограммы основаны на «future и promises»?

Вообще говоря, сопрограммы и future/promise — вещи ортогональные. Можно реализовать сопрограммы без использования future/promise подхода (см. synca: habr.com/ru/post/201826), обратное тоже верно. Можно их скомбинировать и получить сборную солянку, как в вышеприведенной статье.

Однако, как мне кажется, такая солянка ясности это не добавляет. Даже наоборот.
Но конкретно Coroutines TS именно что основан на промизах.

Конкретно CoroutineTS основан на специальных объектах, которые очень похоже на future/promise, но при детальном рассмотрении — совсем не похожи.


Для этого рассмотрим интерфейсы:


template <typename T>
struct my_future {
    bool await_ready();
    void await_suspend(std::experimental::coroutine_handle<>);
    T await_resume();
};

Легко видеть, что тут нет никаких then, onError и т.д. Т.е. это похоже на future/promise примерно так же, как Java похожа на Python.

Так тут await_suspend как раз роль then и играет.

Основная идея future/promise — это композиция вычислений. Мне очень интересно, как с помощью этого интерфейса можно реализовать что-то типа этого:


// Вычисление (2v+1)^2
Future<int> anotherValue = value
    .Then([] (int v) { return 2 * v; })
    .Then([] (int u) { return u + 1; })
    .Then([] (int w) { return w * w; });
Основная идея promise — это разделение вызова асинхронного метода и передачи колбека, а также возможность долговременного хранения промежуточного результата.

Тот факт, что then возвращает новый promise — всего лишь приятный бонус.
Основная идея promise — это разделение вызова асинхронного метода и передачи колбека, а также возможность долговременного хранения промежуточного результата.

Я уже отвечал на это — это фундаментально неверное утверждение в контексте coroTS, т.к. там вся эта логика с названием «future»(на самом деле promise) и иже с ним никакого отношения к «методу», «калбеку», «хренению чего-то» не относится.

Это именно управляющая структура в которой описывается конфигурация корутины и операции управления ею. Что делать в одном случае, а что в другом. Контекст там вообще в coroutine_handle лежит.

Промисом оно называется потому, что это типа некий интерфейс доступа к будущим вычислениям. Просто взяли привычный базоврд. К «future и promises» определённых в рамках С++ — оно отношения не имеет, да и тому же самому в остальных(как минимум упомянутых тут) языках тоже.

Вы ещё скажите, что coroutine_handle — совсем не колбек, а awaitable-структуру нельзя хранить.
Ответ будет, либо будет только загаживание кармы и минусы? Я жду ответ на свои тезисы. Хотите утверждать, что coroutine_handle — колбек? Обосновывайте. Я жду доказательств.

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

Ок, тогда просто прошу реализовать это:


// Вычисление (2v+1)^2
Future<int> anotherValue = value
    .Then([] (int v) { return 2 * v; });

Внезапно окажется, что await_suspend и coroutine_handle — это вовсе не клиентская часть для создания продолжений, я часть, связанная с жизненным циклом сопрограммы.

Ваш код и подход действительно хороши. До сих пор удивительно то, что люди продолжают порождать множество ненужных, чуждых для С++ абстракций. Появился и хайпанул go. Казалось бы — С++ может так же, да и делает так же в той же boost::fiber(и у вас), но.
А ничего, что сопрограммы основаны на «future и promises»?

Выше gridem уже ответил, и ответил правильно. Да и я об этом упоминал. coroTS — это по-сути новая семантика для функции. Она превращает функцию в некий много раз вызываемый генератор, выглядит это как-то так:

auto f = [](auto cb) {
  for(size_t i = 0; i < 10; ++i) cb(i);
};

auto coro_f = [i = 0ul]() mutable {
  if(i < 10) return i++;
  return ~0ul;
};


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

Так же, в контексте С++ future/promise — это всё про потоки(просто переключение контекста, про стек в этом контексте). А nginx и coroTS(на выходе) — это именно про логику, логику в рамках одного контекста.

Почему наружу из coroTS торчат future и promises? Ну это просто модные/привычные слова. Как было сказано ниже — это управляющая структура. Она никак не связана с
позволяющие выражать темпоральные связи между событиями — в текущем моменте времени и в неопределенном моменте в будущем.


И бам, фундаментальная ошибка в логике. И виной тому размытые понятия. С(и С++) не относится к тем языках, которые понимаются под языками выше. С++ может управлять потоком выполнения и никакой ОС для этого ему ненужна.

Что вы понимаете под "может управлять потоком управления"?

Что вы понимаете под «может управлять потоком управления»?

Всё очень просто. Есть некий поток исполнения и так же он является потоком управления, т.к. исполнение кода может изменять то — куда дальше пойдёт исполнение.

В базовой теории управляют этим всякие конструкции вида if/call/return/break и прочее. Всё управление явно описано и код исполняется в том порядке, в котором описано.

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

Поэтому тут возможно сделать read() который попросту стопнет выполнение. Хотя в базовой теории подобное вообще невозможно и именно поэтому там и вводятся всякие управляющие конструкции.

Я всё еще не понимаю о чём вы говорите. Почему в базовой теории вызов read(), который стопнет выполнение, невозможен?
Очевидно, потому что read не является управляющей конструкцией, да и вообще «проблема остановки». Ну в целом зачем спорить — просто можете показать read() описанный в модели базовой теории, да и вообще того же js/scala и что тут ещё упоминалось.

А зачем быть управляющей конструкцией для того чтобы останавливать выполнение?


Кстати, держите: fs.readFileSync

А зачем быть управляющей конструкцией для того чтобы останавливать выполнение?

Оно не может остановить, ведь я говорил, что в базовой теории этого нет. Оно не останавливает, а управляет.

Кстати, держите: fs.readFileSync

Ещё одна фундаментальная ошибка — это С++-функция и никакого отношения к js не имеет. Нужно показать именно функция написанную на языке, в рамках самого языка. А то, что можно из вне стопнуть исполнение — это всем известно, ОС поступает именно так же. vm для js является именно ОС, и стопает ОС.

Я же говорил об остановках в рамках самого языка, а не в рамках внешнего воздействия.

Покажите тогда реализацию read средствами самого языка, без применения ассемблера или средств ОС.
С чего? asm часть языка, никаких средств ОС там нет. К тому же ОС для си — это просто нативный райнтайм и опять мимо. По-сути я могу использовать ОС.
UFO just landed and posted this here
Ну так ноде.жс с плюсовым кодом — тоже просто нативный рантайм.

Неверно — это не нативный, а внешний рантайм. Нативный — это родной для языка рантайм, и родной не в смысле рядом, а всмысле как следствие написания кода на этом языке.

Кстати, делает ли наличие FFI в C в стандарте хаскеля сишечку частью хаскеля?

Очень слабо, т.к. это попытка выдрать один из критерием и пытаться его опровергнуть. Это изначально определяется за глупость.

По поводу аналогий с ffi — это так же глупость и по многим причинам. Во-первых никакой ffi ничего не может, т.к. выполнять он будет в контексте Си, а не в контексте хаскеля. Т.е. никаким образом этот код не позволит остановить именно хаскель-код.

Далее, никакого языка как ассемблер нет — это фантом. Даже если будут предприняты какие-то попытки назвать его языком — они так же обречены на провал, т.к. это язык(если это язык) совершенно другого уровня.

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

К тому же, как я уже говорил — стандарт меня никак не волнует и волновать не должен. Это какая-то попытка защиты от моего оппонента. Я нигде не декларировал что меня, да и вообще кого-то должно это волновать. Да и нигде и никто этого не декларировал, только задним числом и то только для меня, но не для себя.

К тому же, сами эти рассуждения — глупая попытка подменить понятия. Я нигде не говорил о том, что ассемблер является частью си. Я говорил о том, что кейворд asm, т.е. использование ассемблера — является стандартным для си.

Тут нужно ещё раз повторить. asm является базовым интерфейсом платформы. Он никак не может определяться как какой-то отдельный язык и т.п. Кто угодно может использовать базовый интерфейс платформы. Это не запрещается. Этому не может быть аналогий использованию ДРУГОГО языка.

К тому же, ffi предлагает использование ГОТОВОГО кода, а не написания кода. Это вообще не является эквивалентом. asm — это именно встроенный в си асм, встроенный асм. Это декларируется как часть си-кода. ffi не предполагает встраивания си-кода в хаскель, либо куда ещё.

Ах да, для общего развития: gcc.gnu.org/onlinedocs/gcc-8.3.0/gcc/Local-Register-Variables.html. Раз мне пытаются за язык выдать какие-то частные vm и рантайм для них — я могу делать тоже самое.

К тому же существует setjmp/longjmp. Формально остановка уже есть, правда со стеком всё сложно. Но LRV это полнофункциональная фича.

asm — не часть языка, а зависимое от компилятора расширение. Стандарт языка не предписывает для него определенного синтаксиса и поведения.


К тому же существует setjmp/longjmp.

… являющиеся частью стандартной библиотеки, и невозможные для реализации средствами языка.


Ваша фундаментальная ошибка — в том, что вы изначально выбрали С++ в качестве "языка который может", а такие языки как Javascript и, внезапно, Scala — в качестве "языков которые не могут", вывели из этого разделения следствия, а теперь пытаетесь доказать исходное разделение пользуясь этими следствиями.


Хотя на самом деле всё отличие в том, что С++ — многопоточный, Javascript — однопоточный, а Scala вообще пытается быть функциональной.


А потоком управления могут управлять все языки программирования.

asm — не часть языка, а зависимое от компилятора расширение. Стандарт языка не предписывает для него определенного синтаксиса и поведения.

И? Он есть, какое там поведение к делу не относится. К тому же, я заранее свёл на нет все эти попытки юлить в сторону «не определено» тем, что nodejs и есть то самое нестандартное расширение. И у вас два пути. Играть в дурака, игнорируя это обстоятельство. Удалить изначальный коммент как несостоятельный. Либо — принять расширения за язык.

К тому же, все эти рассуждения полная глупость т.к. я нигде не говорил и каком-то языке без расширений — мне он не интересен, как и кому-либо ещё. Именно поэтому вы и начали ссылаться на ноду, а после начали вести двойную игру.

… являющиеся частью стандартной библиотеки, и невозможные для реализации средствами языка.

Опять попытка дёргать левые фразы. Где ответ на LRV? К тому же, вам никто не запрещал используя части стандартной библиотеки реализовать остановку на read() в js.

Ваша фундаментальная ошибка — в том, что вы изначально выбрали С++

Ну дак я выбрал, а после того как я выбрал — мы видим результат. Ваши глупые попытки мунисовать, гадить в карму, игнорировать неудобные тезисы и обстоятельства, вести попросту двойную игру.

а такие языки как Javascript и, внезапно, Scala — в качестве «языков которые не могут», вывели из этого разделения следствия, а теперь пытаетесь доказать исходное разделение пользуясь этими следствиями.

Дак я могу, я и доказал. Именно поэтому у меня есть на С/С++ реализация юзерспейс тредов, а в скале/жс нет. Это аксиома.

Хотя на самом деле всё отличие в том, что С++ — многопоточный, Javascript — однопоточный, а Scala вообще пытается быть функциональной.

Абсолютно неверно. Что значит неведанное «многопоточный» и какого оно имеет отношение к теме — неясно.

Многопоточность это и есть возможность управления потоком управления/выполнения. Когда возможно в какой угодно последовательности и как угодно(хоть в параллель) исполнять код. Кол-во исполнителей вообще никак и на на что не влияет. Фундаментальным является то, что я назвал фундаментальным.

А потоком управления могут управлять все языки программирования.

Вам дали задачу — вы не смогли её решить. О чём можно ещё говорить?

К тому же, тут можно заметить попытку подменить понятие. А ведь я явно говорил о том, что понимаю(вернее о какой части управлять говорю) под «управлять». И я говорил именно об управлении вне управляющих конструкций.

Очевидно, что никакой язык с внешним рантаймом и логикой на лексике в это не может, а такие почти все.
Мне кажется логичным, что функции возвращающие Future должны получать Future в качестве аргумента. Тогда сигнатуры из примера изменятся так:
Future<Request> GetRequest();
Future<Payload> QueryBackend(Future<Request> req);
Future<Response> HandlePayload(Future<Payload> pld);
Future<void> Reply(Future<Request> req, Future<Response> rsp);

Теперь Then не нужен, и функция хорошо выглядит даже без корутин:
auto req = GetRequest();
auto pld = QueryBackend(req);
auto rsp = HandlePayload(pld);
Reply(req, rsp);

Это всё логично только пока вы не пытаетесь реализовать эти функции, после чего вам снова становятся нужны Then и сопрограммы

Sign up to leave a comment.