Well, that's one position on the subject. The Rust people prefer option 3 over option 4. Go takes the same approach. Python prefers exceptions, and the exception hierarchy puts (almost) all the exceptions which result from external problems under EnvironmentError. Much of the trouble with error returns comes from the strange C convention that functions with return values can be called as if they didn't return a value…
Most statically typed languages prefer 3 over 4. For the simple reason that it's easier for a compiler to verify that you've handled all the success and error cases. For Haskell though there exists a library which allows the compiler to track exceptions in much the same way as normal values, and thus warn you when you fail to handle an exception in a particular code path.
It's an elegant way to solve this problem, mentioned in the article:
> However returning error codes makes error propagation difficult. When there’s need to propagate the error up the stack several layers, every layer needs to make sure that they do it correctly... ideally most of the code along the way should be error agnostic and not really need to know about the details of the possible errors but still be able to propagate them up to the caller without a hitch.