$c->run(... 'VM.clone' ...), fn (string $vm) => $c->run(... 'VM.provision', [$vm] ...), ...]) */ final class Chain { /** @param list<\Closure> $steps */ public static function run(array $steps, mixed $value = null): Task { $done = new Task(); $next = function (mixed $value, int $i) use (&$next, $steps, $done): void { if (!isset($steps[$i])) { $done->settle($value); return; } try { $out = $steps[$i]($value); } catch (\Throwable $e) { $done->settle(null, $e); return; } if ($out instanceof Task) { $out->then(fn ($r) => $next($r ?? $value, $i + 1), fn (\Throwable $e) => $done->settle(null, $e)); } else { $next($out ?? $value, $i + 1); } }; $next($value, 0); return $done; } /** A Task that gives $value when $task succeeds (a step whose own result is not what the next step needs). */ public static function keep(Task $task, mixed $value): Task { $out = new Task(); $task->then(fn () => $out->settle($value), fn (\Throwable $e) => $out->settle(null, $e)); return $out; } }