Exceptions can be bad if done the wrong way. But the solution isn’t to not deal with it and put it on the programmer. That’s laziness.
The problems are that the signature of functions doesn’t say anything about what values it might throw, and that sometimes the control flow is obscured — an innocuous call throws.
Both of these are solvable.
Sure but that also feels like a compiler problem. The compiler knows everywhere my function can go. So rather then having it just throw an exception - i.e. arbitrary data - on the stack, surely what's really happening is I'm creating a big union of "result | error[type,type,type,type]" which only gets culled when I add my "exception" handling.
My argument here would be, that all of this though doesn't need to be seen unless its relevant - it seems reasonable that the programmer should be able to write code for the happy path, implicitly understanding there's an error path they should be aware of because errors always happen (I mean, you can straight up run out of memory almost anywhere, for example).