4 ms·
Personally, I don't believe error checking is line noise; it is part of your business logic. Forcing me to acknowledge and deal with error cases requires that I
by jalfresi 12y ago
Personally, I don't believe error checking is line noise; it is part of your business logic. Forcing me to acknowledge and deal with error cases requires that I write more robust code, that I acknowledge the failures and errors that can occur within my logic, as they are part and parcel of that logic.
If you see only the success path as the "true code" then you are missing the whole raft of other conditions that can occur; its an idealised idea of what the code is.
This is why I like Go. Its the first language I've ever used that forces me to think pessimistically about my own code.
- lmm 12y agoScala makes the distinction clear by having the difference between "=" and "<-" (or "←"); the former for something guaranteed to succeed, the latter for a possible error. This is wonderful; it's obvious enough that you see it (and the compiler catches you if you get it wrong), so when an error happens you can immediately see which functions might be causing it and which ones can't be. But you don't have to write a bunch of identical lines; one or two characters are enough for something this common. And you can reuse the same syntax for other error-like contexts, rather than having syntax for this one piece of functionality that's special-cased in your language and no way to handle similar cases (e.g. audit logging).
- eropple 12y agoI don't think you actually read my post before your fingers got going. Monadic comprehension with Try[A] is effectively an Either[A, E], where E is any exception thrown within that context. Mapping over a Try[A] returns a Try[B]; if the first Try failed, it likewise failes, becoming an Either[B, E]. I am thinking about my error case. I am explicitly acknowledging that I am doing so; heck, scalac will fail to compile if you haven't folded across both the failure case and the success case. I'm just not writing the same (stupid, wordy, potentially failure-prone) boilerplate code every time I make a method call into a library.
- jalfresi 12y agoApologies, this is my ignorance re: scala but I have no idea what you just described, although to these ignorant eyes it appears to be a lot more complicated to read and understand than a simple if err != nil check! however, I will go and google some of the concepts you have described to try and understand better your point
- eropple 12y agoIt's not complicated. Your return value can, as in Go, be an error or an object of type T; unlike Go and its stunted type system, this is a generic object and so this must be written once for all types. That return value can be functionally mapped over, just like `map` in Ruby, and will return either the original exception (the mapping function will not be called) or the result of the mapping function. These can be chained indefinitely. At the end you have a Try object of the generic type of the final mapping function, against which you can pattern match for Success or Failure. (Failing to compare against both Success and Failure is a compiler warning. There's a flag, IIRC, to make it an error, but since my IDE is already yelling at me that I screwed up I don't in practice have this problem.) Additionally, at any point in the chain you can call `recover`, which is similar to `map` but for the exceptional case (Try[T] -> Try[T], with Success[T] passed through) if it's a recoverable error. All error handling--as separate from error recovery, which is rarely in practice useful--is always, always, always at the very bottom of the function, right next to the happy path. It literally is impossible to miss, and does not require copy-pasting code after every library function that can fail. There's no reason to check 'err' every time unless your language is just straight-up too dumb to do work for you. Your time is more valuable than the computer's in all but the most extreme of cases; you should act like it.