More lines of code is inherently a bad thing, while I'm all for simplifying the code so that people can understand it, I'm not a fan of doing that at the cost of increasing the amount of maintenance that will be needed on the code base overtime.
More lines of code is inherently a bad thing, while I'm all for simplifying the code so that people can understand it, I'm not a fan of doing that at the cost of increasing the amount of maintenance that will be needed on the code base overtime.
That is the single greatest typo I have ever read.
Pretty sure this will get flagged, and I hate myself for it, but it will make sense when you review that post again.
All lines are not created equal.
if err != nil {
return err
}
Those 3 lines have very, very little maintenance cost.The obvious must also be mentioned: less lines of code usually mean building on abstractions - the lines of code are in there, somewhere, but not in your code. Better hope that abstraction is well-tested. Of course it's all a matter of balance, but I disagree with "more lines of code is inherently a bad thing", which stated another way, would mean "cram as much information as possible in a single line of code".
In the end, lines of code are a bad metric of software.
These three lines are everywhere, give me no clear indication of whether they are intentional behaviour or merely a boilerplate-by-convention, and worst of all, they obscure the actual business logic.
Why doesn't lack of generics kill Go? Well, if you're working in Go's wheelhouse, []byte actually covers a lot of things and you're rarely that far away from it, because you're about to read or write a []byte real soon now. (I don't mean you always have one literally in hand, you probably marshaled in into a struct, just that you're often very close to either reading or writing it.)
Why does it work to have this error handling in Go? Well, when writing network servers or other basic cloud infrastructure, actually a lot of the errors require different handling on a case-by-case basis. Big ol' exception blocks around the whole operation are often hiding bugs, or at the very least, suboptimal exception handling. By contrast, when you're not writing the code to do all the nitty-gritty network operations it's a level of detail you don't want or need.
I think every single posted "success story" I've seen for Go has been a network server. I don't think I've ever seen one about how it really cleaned up my Android app, or how my game development was crashing and burning until I switched it to Go. I don't think this is a coincidence, nor do I expect to see one anytime soon, the recent Android support notwithstanding.
Other languages (e.g. Java) have solved this problem quite elegantly over a decade ago with the introduction of Checked Exceptions[1].
Since this is the pattern that every Go program ends up emulating anyway, Google could save everyone a lot of work by just baking it into the language.
In the eternal words of Larry Wall:
The computer should be doing the hard work.
That's what it's paid to do, after all.
[1] https://en.wikipedia.org/wiki/Exception_handling#Checked_exc...For example,
But now the consumer needs to know multiple idioms and recognise when they are being used. And the reader of the consumer code needs to know that the normal `err != nil` idiom is not being followed. All of which requires everyone involved to do more reading of source code than a "throws" line.
How is this simpler than having a single, universal concept of exception handling?
The "helper" function there introduces 10 lines of code (per function in which you want it), and doesn't even work if you call anything besides that one `write` in sequence.
It doesn't make the guard clauses disappear. It merely forces the programmer to manually aggregate them at a higher level with even more boilerplate code.
if ( somethingICanDoNothingAbout)
{ throw new UncheckedGameOverException( "You're screwed!") }
... // top level event/request/message handler, many layers up
catch ( Throwable e)
{ log.error( "Game over:", e); ... }
(I'm with the Anders/C# camp on this one, as much as I dislike MS otherwise)The inclusion of Unchecked Exceptions is one of the biggest warts on the Java language.
I should clarify: The wart is that programmers can introduce their own unchecked Exceptions. This should not be allowed since, as you say, unchecked Exceptions only make sense for unrecoverable runtime errors.
Checked exceptions should only exist in situations where the caller can definitely recover from them.
False dichotomy. Unchecked exceptions become part of the callee's method signature, i.e. the contract that the caller must fulfill.
Arguably almost nobody does the latter correctly when choosing checked exceptions
Baseless claim. I see many people using Exceptions correctly and elegantly, for error handling and flow control.
and the consensus is
Opinion != consensus.
For example, a function to parse a string into a number should return both a number-reference, and a flag of some kind (or maybe just an Option). Crap input should not generate an exception like out-of-memory or an I/O error. Trying to dereference the number w/out checking to see if the string was parseable COULD generate a not-mandatory-to-check exception, though.
I guess there is a certain amount of tension between the idea that functions/methods should document the kind of errors/exceptions they might have, and between the annoyance that is all the crappy do-nothing catch clauses that turn around and re-vomit wrapper exceptions that ultimately land in a "well, it didn't work" log message and punt, rather than really "handling" the original exception in any way, shape or form.
foo, err := somebullshit()
if err != nil {
// bleargh
}I feel as though I've experienced a very slight but irrevocable grand mal seizure.
That's why it's nice when the language provides tools to make it more likely to be correct.
And it's not like these two are the only possible error handling strategies.
You can't have your lunch and eat it too: if you want robust code, you -- the developer -- need to spend some time thinking about how you manage your errors. Go makes it all too easy to sweep those under the rug.
Totally unnecessary, a solved problem eons ago through very manaeable, existing constructs that have a lot more semantic meaning.
(Though I wouldn't characterize it as "cramming"; better languages save lines by not requiring programmers to specify irrelevant detail rather than by packing more information into the same space).
It's the same problem with copy/paste code. With copy/paste code, it often has small changes that you can miss easily. You have to read every line of code to not miss the small variations. If the person bothered to generalize the copy paste code a bit, you can cover all cases in one spot, and be less likely to miss problems.
Does it?
`if err != nil` is a convention. It's not a checked error. It's like saying "C has explicit array bounds checking", if the convention was to check it by hand.
For all the hate Java gets, when I have a checked exception, I know what it is going to be. I don't need to dig around in sourcecode trying to work out if I should care or not. I don't need to check which of the three Golang error-distinction idioms (package-variable errors, error subtypes, plain-old-errors-with-magical-strings) is in play.
As you can guess, I don't like Go.
It's... obviously both.
The advantage of runtime exceptions over Go is that I will get a fatal crash and a stacktrace. I don't have computation proceeding in an undefined, difficult-to-debug way because I forgot to do `err != nil` somewhere (particularly for functions that only return err).
Don't get me wrong, though. I become grumpy when I see empty catch clauses. Or a logger figleaf.
No, it doesn't (simple example: the return value of os.Setenv). errcheck exists for a reason.
So that's what we're calling the lack of HoFs these days? It's really sad to hear that anyone considers map/fold/filter are "overly-clever".
Tim Sweeney (Epic) says[1]:
"“For” loops in Unreal:
40% are functional comprehensions
50% are functional folds"
I hardly see how changing those loops into a construct that more accurately represents them is overly-clever.
Go has HoFs. The lack of parametric polymorphism is what prevents you from writing `map` in Go (subject to several caveats surrounding efficiency and type safety).
Please tell me that was autocorrect and not muscle memory