3 ms·
I'm not arguing against the power of the error classes. I argue that you're turning python into golang, whose error handling is arguably worse than python or J
by bb88 4y ago
I'm not arguing against the power of the error classes. I argue that you're turning python into golang, whose error handling is arguably worse than python or Java.
> How Python people deal with the fact that simple things like dictionary key lookup or URL parsing raise exceptions without thinking of that as a language-design error, I'll never understand.
"if "x" in dict", or "dict.get("x")" works really well. But again, if you want the exception, it's there. "dict["x"]" will raise.
You also have sets in python so you can do stuff like this:
potential_keys_in_d = set(d.keys()).intersection(potential_keys)
potential_keys_not_in_d = set(potential_keys) - set(keys.d())
A lot of production code uses urlparse() and it raises ValueError. https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse https://docs.python.org/3/library/urllib.parse.html#urllib.p...
> Or, at least, it shouldn't be throwing any exception your caller would ever expect to need to catch as part of the semantics of using your library.
So your code does 1/0. What should the language do? raise an exception? return NaN? Panic? Core dump?
I ask because 1/0 is perfectly recoverable in most cases. In C it's undefined (core dump anyone?). Golang panics. Python and java raise an exception. And javascript returns ... well ... something worse.
> What Python calls "errors" and Java calls "unchecked exceptions" — e.g. MemoryError and the like — are fine to let "leak out" of pure functions, because those exceptions aren't really intended for the direct caller to handle.
From the standpoint of a being a pragmatic programmer, the language just needs to choose one error channel and let that be the primary form of error passing.
Golang has two error channels, panics and return values, and I'd argue in this respect it's worse than Java.
- derefr 4y ago> A lot of production code uses urlparse() and it raises ValueError. https://docs.python.org/3/library/urllib.parse.html#urllib.p https://docs.python.org/3/library/urllib.parse.html#urllib.p... Yes, that was precisely my point: I don't understand why people are okay with this. URLs being invalid isn't an error; it's expected. If you're constructing a URL from a string, you haven't yet validated the URL. So why would it be exceptional for the validation to fail? I would expect explicitly calling a parser function, to give me the sum-typed result that the parser produces: either the successfully-parsed data, or the relevant shift/reduce failure. Consider: when I call urlparse(), I'm not demanding that the runtime construct a URL from the string. The function is not called `URL()`, like it is in Java (where IMHO it becomes perfectly justified to throw an exception — you expect `URL()` to give you a URL.) Calling a parse function like `urlparse()` does not imply that the thing I get back is going to be a URL. It implies that it's going to be the result of parsing, whatever that is. Just like, when I call `requests.get()`, I expect that the thing I get back is going to be the result of the request, whatever that is. > I ask because 1/0 is perfectly recoverable in most cases. It is recoverable, but only locally. No caller has any idea what to do with the fact that the callee divided by zero. It's a case where doing something and then not immediately handling it in the same local scope, is a programming error on the part of the library author. > What should the language do? raise an exception? return NaN? Panic? Core dump? Certainly, in dynamic languages like Python, programming errors in libraries have to be dealt with somehow. But it doesn't really matter what particular mechanism they use, because they shouldn't be recoverable by the caller. The callee failed the runtime's preconditions — like doing a use-after-free in C. The whole runtime is now poisoned, because the library, failing to handle this failure, very likely left its internal state in some indeterminate half-mutated form. This is just a too-late equivalent to a static language's "this fails to compile." So aborting the entire runtime — or doing anything else that means "this is unshippable as-is" — is probably the right choice. (Maybe delete any generated .pyc file for the module the code was defined in, while you're at it.) Maybe use a mechanism that can be caught by the runtime itself — if the language abstract-machine includes some sort of mobile-code-execution-sandbox abstraction like V8's isolates. Because programming errors in sandboxed user code shouldn't kill the host-runner. But that's the only case I can see where it'd be relevant. Ideally, in general, a dynamic language should do enough static analysis at load time to detect programming errors, such that it can have `import` itself raise, refusing to poison the environment with a module with axiom-invalidating semantics. (Though, in the case of integer division specifically, we've really just screwed ourselves over in most languages by making it a binary operator with a single integer result upon untrusted input. IMHO languages could do a lot better, by 1. having an explicit "integer divisor" linear-type that cannot be zero, and only allowing integer division by that type, such that you have to construct that type first; and/or 2. making integer division by regular can-be-zero integers a branching operation, where you must always define the code path for when the divisor is zero.) > Golang has two error channels, panics and return values, and I'd argue in this respect it's worse than Java. I disagree. Golang has one "error channel" — return values. You should never see/catch a panic from a callee. You should be able to just forget panics exist — similar to Errors in Java, or `error`s in Haskell. Golang panics have two uses: 1. the same thing I described above — unrecoverable operational states and runtime-detected programming errors in libraries. Abstract-machine axiom violations. Callers aren't supposed to handle/`recover` these. Nobody's supposed to touch these; they're supposed to just let them bubble, until the program halts. It doesn't matter that these are implemented via `panic` — they could just as well not bubble at all, and terminate the process on the very instruction they're raised on. They just-so-happen to use `panic` so that deferred functions get called for cleanup, so that sockets aren't left hanging open, files get flushed to disk, etc. 2. structured goto — like Ruby's `throw`+`catch`, (which have nothing to do with its exception system, which uses `raise`+`rescue`), or Common Lisp's `call/cc`. Golang JSON parsing, for example, uses `panic`+`recover` as a way to break out of parser stack frames until it hits the toplevel frame of the parse routing; so it can then immediately return a parse error to the caller. These are never supposed to leak outside the design of a library; so you're not supposed to be handling these, either. Also, this isn't error handling, per se; this is an optimization over returning a condition, then checking for that condition in the caller and breaking out of a loop and returning the same condition further, etc. It could just as well be used to signal a condition like "I'm done, I found what I'm looking for" as "I failed to parse." It's infrastructure, mechanism, that says nothing about the semantics of what it's trying to do or what type of information it's carrying. (Personally, I would argue that this should have been given a separate name from `panic`; and that `recover` should have only been able to catch these, not the axiom-violation panics of #1. But I understand that Golang's designers are big fans of avoiding special-purpose semantic primitives in favor of general-purpose "mechanism" primitives — as can be seen in their previous design of e.g. Plan9's everything-is-a-filesystem "mechanism" primitive.)
- bb88 4y ago> URLs being invalid isn't an error; it's expected. Of course it is. Would you report back to the user: "Expected Behavior: Invalid Url."? I bet instead even you would write: "Error: Invalid Url". >> I ask because 1/0 is perfectly recoverable in most cases. > It is recoverable, but only locally. No caller has any idea what to do with the fact that the callee divided by zero. f(x) = x+1/x shouldn't crash your space station life support because it's undefined at 0. Just like tan() is undefined at pi/2. As you said, it's expected.