Wlad писал(а):
Может вы пытаетесь найти общее, там где его не стОит искать и применяете подходы с чужой "внутренней логикой работы"?
Нет ли большего смысла отображать (в императивных языках) "постадийность прохождения" именно постадийной проверкой (для случая "однопотоковых" задач) и переходами между состояниями (параллельно работающих автоматов в "многопотоковых" задачах)?
Возможны сценарии, когда рукопашная рутина со сплошными проверками/защитами на каждый чих (в т.ч. и дублирующимися, с паразитизмом побочных эффектов на and/or-операторах) также может оказаться и утомительной, а то и запутанным УГ (в случаях комплексной "бизнес-логики"). Решения с использованием досрочных выходов (return, break, exit и т.п.), механизмов исключений, плюс finally/defer и т.п. могут оказаться оправданными "производственной необходимостью" (и эффективнее), но с нарушением канонов "структурщины".
Параллельная композиция процессов -- альтернативное решение в помощь к последовательной композиции действий. Здесь, видимо, необходимы уточнения. В самом деле, выше был пример из ForeC, где "параллельные" функции (задаваемые через оператор "par") есть именно реальные параллельные процессы (асинхронные со своим исполнителем или потоком, и примерчик был под контекстом именно вычислительных (не "управляющих") алгоритмов).
А, в целом, речь о "реагирующих" системах, с параллельностью как совместно исполняемые процессы, понимая последнее широко: процессы со своими исполнителями (потоками), кооперативная многозадачность, с алгоритмами для одновременных вычислений (ускорение) и "управляющими" алгоритмами, с событиями/сигналами не только из внешней среды. Возможны разные формы представления (как и "аксиоматизации").
Ниже ещё один пример на Esterel-вариации (Esterel в первичной основе есть однопоточный) -- на языке из
проекта Céu -- некая полуакадемическая игрушка-эксперимент реализации подмножества Esterel c Си-шными мотивами и немного от Lua (на выхлопе генерируется код на Си и/или Lua). В тех краях "классические" операторы "abort ... when ..." не используются, а параллельные действия могут "вытеснять" сами себя через уточнение семантики дивергенции/конвергенции процессов (что имеет свои преимущества):
Код:
...
input int ENTRY; // вход (входное событие)
var FILE* f = <...>;
var char[10] buf;
event int read; // внутренние события...
event void excpt; // ...
...
// Оператор "par...with...end" -- параллельная композиция действий,
// в данном случае в варианте с "or"-семантикой соединения
// процессов: весь блок завершает работу по окончанию исполнения
// хотя бы одного из процессов
par/or do
loop do
var int n = await ENTRY; // ожидание входа
emit read => n; // генерация события "read" со значением "n", при котором
printf("line: %s\n", buf); // осуществляется переключение контекста на операции await
end
with
loop do
var int n = await read;
if read(f,buf,n) != n then
emit excpt; // генерация события как возникшее "исключение"
end
end
with
// первый вариант реакции на "исключение":
loop do
await excpt;
buf = <...>; // некое значение по умолчанию
end
// или же такой вариант -- после "приёма" события данный процесс
// завершиться, вместе со всем par-блоком:
// await excpt;
end;
...
Выше процессы не являются "многопотоковыми", аля корутины (в этом языке истинно параллельные, асинхронные, действия определяются дополнительно и имеют свои семантические особенности). Фактически, упрощённо, данный фрагмент процедуры/программы есть "препроцессор" для построения последующих императивных if-ов (или goto и т.п.).
В примерчике акцент, прежде всего, на принципиальной модели вычислений. В данном случае "основной упор" на эмуляции "исключений" как локальных внутренних событий. Совместно исполняемые действия (которые могут "аккумулировать" у себя своё состояние) сцеплены по контексту (не разнесены по "активным объектам", которые также возможны), как аля секции try/catch. Причём, как бы, эти "секции" могут прерывать или корректировать (взаимодействовать), по мотивам, например, техники "conditions and restarts" в Lisp-ах.
Последнее могло бы быть эмулировано в мэйнстриме, например, через дополнительные операторы для исключений:
http://axisofeval.blogspot.com/2011/04/whats-condition-system-and-why-do-you.htmlИли пример ухищрений на макросах в Rust (видимо, такое стандартное решение также "не взлетело"):
https://static.rust-lang.org/doc/0.8/tutorial-conditions.html#conditionsИтого в отношение поступательного ("монадного") процесса. В общем случае последовательная композиция функций/действий (понимая упрощённо и предельно широко: с альтернативами и повторениями, т.е. с цикличностью) может быть подкреплена недетерминированной параллельной композицией операций, управляемых возникающими в процессе событиями (внутренними, в т.ч. и повторяющимися. Взаимодействие асинхронных процессов, вычислительные (не "управляющие") параллельные алгоритмы, управление на основе событий от внешней среды и др. -- отдельная дополнительная, однако связанная тематика).