> Why do we need to have ambient control flow?
Because, properly-used, it saves you a lot of effort.
Returning an error type unwinds the stack. If I have a low-level computation that has a number of error cases, and I want the high-level code to intelligently handle some of those error cases and continue processing, that's not doable using return values. Error codes bind the decision of which error-recovery strategy to take (which is only present at a high level) with the details of that strategy (which is only present at a low level).
As a trivial, obviously-fake example - if I have a high-level GUI library that makes use of a low-level division function, I might occasionally divide by zero. Depending on what the GUI library was doing, I might want my divideBy(x, y) function to return 0, 1, the first argument, the second argument, or not return anything because that section of the code will be completely aborted.
Without ambient control flow, if you just have return values, you have to check the return value for every single division operation you perform - and you'll have to re-implement code paths where a division operation failed.
What if you have a long-running operation? If you return an error value when that operation happens, but it turns out the nature of the operation allows you to ignore that error and continue, you'll have to re-start the entire computation. If you hard-code the lower-level logic to ignore errors and continue, then you'll also end up ignoring errors that you really shouldn't.
Error types do not solve these problems - in fact, they require that you duplicate lots of code to, say, make multiple variants of a library that are identical except when it comes to error-handling - or just cause your applications to lose lots of error-handling nuance and bail early on lots of exceptional circumstances that could be recovered from.