6 ms·
There’s two types or reason for a program to error: - the code has failed and the developer needs to be informed what to fix - the environment has failed and
by laumars 5y ago
There’s two types or reason for a program to error:
- the code has failed and the developer needs to be informed what to fix
- the environment has failed and the user needs to be informed
Exceptions are for the former, which Go still supports via Panic(). Errors are for the latter. Which is why all the boilerplate Err code creeps in.
I don’t like getting stack traces from applications when I’m a user. It always feels to me like the application is only half finished. When the error is environmental (eg file system permissions) a stack trace just muddies the water with unnecessary output.
This is why I like Go’s distinction between errors and exceptions.
- zarzavat 5y agoThis is great in theory but in reality no such distinction exists. For example, is trying to read a missing file a failure of the code, or a failure of the user? It could easily be either, the developer hardcoded the file path wrong during development, or the user selected a wrong path in a CLI, ... the distinction between errors and exceptions must made by the call site, not the definition site. The code calling readFile() knows whether the error is recoverable or not. readFile() itself does not, so the same mechanism should be used for both errors and exceptions. All this was figured out decades ago, that's why exception systems were invented, but language designers keep making the same mistake over and over again in an effort to simplify the unsimplifiable.
- atomicity 5y agoExcept that people are too lazy to use exceptions that are checked at compile time. The main benefit of Go is that the people that defined the library ecosystem actually decided to handle errors. It's the bare minimum, but it's better than just forgetting that the error exists and not documenting it either. The language is ... not the best, but the libraries tend to be more robust.
- erik_seaberg 5y agoIt’s not always laziness, in Java stream.map(f) doesn’t allow f to declare any checked exceptions. We could catch and wrap everything but that obscures the useful code without any improvement in safety.
- tsimionescu 5y agoLibraries can almost never handle errors - they can only signal them. And Go libraries are particularly bad at this, because they almost always return error strings, instead of meaningful error types, which means you can't easily handle a subset of errors. Even if you did, Go's errors suffer from the same problems as exceptions in many languages: there is no way to document what errors your function can actually return, just that it returns some error. What language are you thinking of when saying Go libraries do better at error handling?
- laumars 5y agoIn your example, it should be an error because it’s an environmental problem (could be file system permissions). Sure, it could be a developer fail where they hardcoded the wrong path (surely any kind of testing would pick this up?) but it’s still an error in the environment where had the path been correct it could fail due to something the user misconfigured. So it needs to be handled as a user facing error. Exceptions do exist in Go, it’s called “Panic”. This whole “Go doesn’t have exceptions” meme is completely untrue. It’s just not the preferred way of error handling because, unsurprisingly, most application errors are going to be environmental. But if you select an out of bounds index in a slice or don’t handle a nil pointer correctly you’d get a panic — because those are clearly developer issues that warrant a stack trace rather than environmental problems that need a friendly user facing error message.
- zarzavat 5y agoIf you make it an environmental error then the programmer is likely to write an error handler that ignores the error because it can "never" happen and the compiler is forcing them handle it. You now need a linter to prevent this and a way to signal to the linter that "no I really intended to ignore that error, I promise" Whereas exceptions fail safe, ignoring the error means not catching it, which means if the error does occur, it will load up the debugger. This is the whole Java "checked exceptions" misfeature again.
- laumars 5y agoThis makes no sense. Plus naff developers can be just as lazy with exceptions if they wanted. A try/catch block can be abused extremely easily.
- konart 5y ago>Exceptions are for the former, which Go still supports via Panic(). The panics are there to be used only in cases where the further code execution is impossible. Not to inform the developer about an error. Errors are a general solution to report an unexpected event during execution. The destination of such report depends on the part of code the program failed.
- laumars 5y agoI get that what you’ve posted is the official description but it doesn’t really explain the distinction well IMO. Eg there are plenty of environmental reasons you’d need to cease execution but giving the user a stack trace isn’t helpful. So be describing the two as who the target recipient of the error is helps. Or at least I think it does. Personally my one gripe with Gos errors is that EOF is handled as an error.
- konart 5y ago> Eg there are plenty of environmental reasons you’d need to cease execution but giving the user a stack trace isn’t helpful. Not sure I got this part. If you want to stop the program and don't to print the usual panic messages into the console - you can log\print whatever you want and call os.Exit(). At the same time you can log stack trace without panicing. >Or at least I think it does. Maybe, it's just that "the code has failed and the developer needs to be informed what to fix" applies to errors just a much as to panics. The only difference is that an error is the situation during the execution where you can continue the job and a panic requires execution to stop (in most cases). At the same time panic are 'developers only'.
- laumars 5y agoYou’re arguing the same points I’m making :)
- simiones 5y agoYour entire argument rests on the weird assumption that the User is responsible for the environment. In the vast majority of software used today, that is not the case. In SaaS, mobile, and appliances, the environment is entirely or almost entirely controlled by the programmer - a missing file is the developer's problem, and the user can't do anything about it. In enterprise software, there is a third entity, the Administrator, that has (almost) full control over the environment, and usually there are layers of 3rd party software that control it directly. A missing file or permission error is useless to the user, most likely it needs to be logged somewhere that Admins check, and the user simply told to contact their Admin. Finally, even in personal desktop software, the environment is jointly owned by the user and the developer - the developer is normally responsible for setting up the initial environment through some kind of installer (msi, deb, make configure etc.), and many environment issues are bugs in the installer, not user errors. Of course, the same library may very well be used in all of these vastly disparate deployment scenarios - the library can't decide what is the source of an error, so making it arbitrarily decide between two possible kinds of errors is wrong. Note that even the reverse is not clear; an index out of bounds error could be a user problem - if they are trying to access the 7th element of a 5 element list. The fact that you'd normally do if userSelectedElement > len(arr) { return nil, fmt.Errorf("Array index out of bounds") } return arr[userSelectedElement], nil Is only because of the convention you were proposing - `return arr[userSelectedElement]` does the same thing alone, if we disregard the convention (in a memory safe language, of course!).
- laumars 5y agoYour argument is just reinforcing the reason for errors to be concise and to the point rather than displaying a stack track when it’s not the developers who are administering the application. Trust me, searching through a stack trace on a centralised logging system isn’t fun. > Finally, even in personal desktop software, the environment is jointly owned by the user and the developer - the developer is normally responsible for setting up the initial environment through some kind of installer (msi, deb, make configure etc.), and many environment issues are bugs in the installer, not user errors. Desktop Linux, yeah. It’s seldom that simple in servers though. SELinux, custom config, custom iptables rules, network wide UIDs (eg shared storage volumes), there’s so much that can go wrong the moment you do enterprise. > Note that even the reverse is not clear; an index out of bounds error could be a user problem - if they are trying to access the 7th element of a 5 element list. The fact that you'd normally do If you’re writing software that doesn’t do input validation and bounds check then you’re a failure of a developer. Sorry but this is the bare minimum I’d expect a developer to do.