5 ms·
The very concept of "error handling" is absurd. There are no errors, just unnecessary abstractions and control flow hacks. You try to open a file; either you c
by enriquto 6y ago
The very concept of "error handling" is absurd.
There are no errors, just unnecessary abstractions and control flow hacks.
You try to open a file; either you can or you cannot, and both possibilities are equally likely and must be handled by normal control flow in your program. Forcing an artificial asymmetry in the treatment of both cases (as championed by the error handling people) adds ugly complexity to any language that tries to do so.
- dllthomas 6y agoCan you name a language/ecosystem that gets it right?
- jjjensen90 6y agoThough I don't think it's perfect, languages like Go that treat errors as simply another type and you check as a return value do get closer to treating errors and success symmetrically versus an exception throwing and typing system like some other languages.
- monocasa 6y agoBuilding on that, the ADT languages with full option types here are fantastic here, because the shape of the data in each case can be taught to the compiler.
- dinosaurdynasty 6y agoRust seems pretty close to this (with Result<T, E>), though there is also the panic system.
- platz 6y agoit's trivial in any language to define and use a Result<T, E> type.
- aw1621107 6y agoThat depends a lot on what you mean by "use". Pretty much every commonly used language would be able to define something that can hold one of two things and provide ways to determine which one is present and retrieve one of the two options, but comparatively few are going to be able to provide the same guards against accidental or intentional misuse that pattern matching gets you.
- saagarjha 6y agoNo, not really. Languages that don’t really embrace such a type can usually never make its use ergonomic. Adding such a construct to C or Java would be “trivial” but its use would be exceedingly painful.
- The_rationalist 6y agoHere's an example of a result type in Kotlin implemented as a library monad. (the language is getting an official one but is currently less featureful than this lib) You can use exception or Result depending on when it makes the most sense and this is the best of both worlds. https://github.com/kittinunf/Result https://github.com/kittinunf/Result
- saagarjha 6y agoSwift had a bunch for a while too, but it’s getting an official one as well. Again, that’s only because the language provides the right tools for it to exist, in languages where you don’t have this you’re going to have a very hard time trying to add these things in.
- vvanders 6y agoErlang / OTP.
- gary_0 6y agoThe problem of error handling arises due to type systems and function call semantics. data = open("/var/foo") Most programmers are going to expect 'data', the result returned by 'open', to be a type that allows reading/writing the file. The programmer expects /var/foo to exist, or they might have checked before calling 'open', but even that's not foolproof. Historically, a failure might just set 'data' to an invalid value (like 0 or null) but that ended up being a bad idea. And we needed some way to return more information about the error. So we started doing this: error = open(data, "/var/foo") But this mainly just complicated things. Is 'data' input or output? The function doesn't return its actual output. And it's still possible to ignore 'error', so 'data' is still potentially undefined. Then exceptions were invented so we could use proper function call styles again, and the program wouldn't go into an undefined state. Instead, the error could be handled with separate logic, or the program would halt if it was ignored. This was far from a perfect solution, though. Then sum types entered the mainstream, so 'data' had well-defined ways of returning something other than the expected result. But that resulted in a lot of competing conventions and stylistic decisions for what to do when 'data' is an error type, that haven't quite been settled yet.
- jjjensen90 6y agoAgain referring to Go's imperfect but interesting handling of this problem, the style in Go would be: data, err := open("/var/foo") if err != nil.... In fact, the compiler will make you deal with both values after assignment unless you explicitly ignore one of the return values.
- wvenable 6y agoI completely disagree. A program or function is designed to perform an operation. If that operation requires the contents of the file, then the program cannot continue unless it successfully reads the contents of the file. There is already a natural asymmetry. If you cannot open the file, there isn't any more to do.
- Mikhail_Edoshin 6y agoAn "operation" is not something inherent to the code. If we look at a function that may get what we call an error, we'll see that in either case it completes and returns control to the caller. We label one such result 'success' and another 'failure' because we also have an idea of purpose of the function, but the purpose does not exist at the code level. Maybe this is why we struggle with errors.
- uhoh-itsmaciek 6y agoThat's a neat observation. If you're writing a function as "How do I get from A to B?", that's more error-prone than "What are all the possible outcomes of trying to get from A to B?"
- wwright 6y agoI disagree; programming languages and code are built explicitly and exclusively to perform a function. Operations and purpose are more inherent to the code than their mathematical/logical nature.
- Mikhail_Edoshin 6y agoYes, everything is built for a purpose. But the raw materials used to make a thing do not change because of the purpose. They follow their own laws, it's us who ascribe the meaning to the resulting combination. Laws are something we cannot change, only use. But purpose is a concept. We do need concepts to create and operate things, but if we shouldn't mistake them for laws. Concepts are something much more pliable :) It's all rather abstract, I guess, but yesterday I found an example while playing a game, "Opus Magnum". You are to solve puzzles by arranging mechanical arms to move marbles. Initially I mentally referred to actions of the arms as "taking a marble" and "placing a marble" and solved quite a few puzzles this way. In all solutions all my arms did exactly that: they took and placed marbles without doing any purposeless movements. Yet yesterday the next puzzle looked too tiresome to built. I had an eureka moment once I realized that the arms do not take and place marbles, they merely close and open their grips and if there's a marble, it gets caught, but the arms themselves have no idea whether they're moving a marble or not. And once I allowed myself to let them do "purposeless" movements without marbles, I found a simple and elegant solution :)
- camgunz 6y agoI agree. You don’t open a file, you try to open a file. When you get a handle back, that’s your library skipping steps.
- TeMPOraL 6y agoThis way a combinatorial explosion lies. Each library/system call you do can result in a set of possible consequences. We usually don't care about them equally, though: in fact, in the file example, 99% of the time we care about whether the file was opened or not - and in the latter case, we don't need to know why. So the asymmetry is already introduced by the intent of the program - there's usually only one path of execution we want; other ones are distractions. Error handling exists to express that asymmetry of caring at tool level.
- renox 6y agoOk, then replace all additions in your program with a function returning either an error or the result. Same with logging statements. And don't ignore the errors.. Is your program still readable?