Recently I had to learn the Go programming language and now I want to share my thoughts from the perspective of a Java developer.
We decided to cr...
For further actions, you may consider blocking this person and/or reporting abuse
I write a lot of Go, I agree with most of your assessment, although I think Go was never really designed to live in an IDE, I use vscode but havent touched the refactor stuff, the Go cli to me is home π .
Also I disagree that returning errors makes the code more unreadable, less pretty sure, but not more confusing, I think if youre used to wrapping everything in try-catches and explicitly defining all errors in and out of your functions then Go's way may seem repetitive, but fundamentally it's simpler, call a function get some value(s) back, theres no extra things popping out at inconvenient times (unless it panicsπ ), I find this to be a lot easier to explain to beginner developers. Again I wouldn't say it looks better, but I think its pretty clear what's going on.
I think the main point is that with exceptions you don't need lots of try-catch blocks all over the place. You only add them where they are relevant, while the rest of the code can be unconcerned with them, which isn't the case when you need to bubble up an error.
That is exactly my point. If I don't need to check/expect an error, I just don't do that and I know the program/thread will crash with a stack trace. If I don't have to write all the error handling and return them to the calling function, my code becomes cleaner and easier to read. Like in this example:
wait so you call functions that could throw exceptions but dont handle them until you find that they do throw? I was under the assumption you would still always check but would just prefer to do that check at a higher level (ie. wrap
doJob
in some error handling), but if you want to ignore errors just don't check them:lmk if im not understanding you correctly
Of course, it is not that I totally ignore all errors/exceptions, I want to handle them (as you have written) "at a higher level". Probably also I don't always know all the exceptions that can be thrown.
And if I ignore an error like in your example, my program will continue to work i.e. all functions
doFirstTask
,doSecondTask
anddoThirdTask
will be executed. I want mydoJob
function to fail after the first error. I can achieve that withpanic
but it is not a good solution too and not idiomatic.Think of it like this: your code is made up of lots of functions calling lots of other functions that are calling lots of other functions. If something goes wrong somewhere, then it's not necessarily the function right above that one that's best placed to handle the error. A thrown exception allows the error to bubble up automatically until the level on which it makes sense to handle the error.
For example, if I have a complex algorithm that at some point does a division by zero, then the code calling the complex algorithm will need to e.g. use a fallback or notify the user of the error. In either case, it's not the algorithm function right above the error that is able to do anything useful with the division by zero error.
I see what you mean, in cases where I don't want to check and wrap an error that would be convenient
Go is a really weird one. I was excited at first, because I'm severely lacking a low-level programming language in my personal portfolio (I can do C if I have to, but I don't like it because it's so blatantly unsafe). But then I tried it and... oh man. The error handling is straight out of the previous millenium (by now, we do have fancy things like, you know,
try
-catch
-finally
...) and the language has many awkward choices. Back then when I used it you were still limited to a singleGOPATH
(I've heard that's different now) and package management (in particular: package version management!) wasn't even close to the reliability and quality I was used to in Maven / Gradle.My absolute worst experience with Go was when I had to parse some JSON data into a struct, analyze it, update it, and serialize it back. It was such a pain. Oh yeah, and no generics on top.
Go does have some stuff going for it though. Being able to produce an executable for any desired target platform independent of the system you're currently running on is impressive. Build times are quite fast and modularity is built-in from the start. The interface duck-typing is something I can get behind because it's compiler-checked and an interesting approach. And the compiler is really really rigid when it comes to code style - an unused variable is an error. Those things I remember as positives, but overall I'll stay away from it for the time being.
Frankly, I love checked exceptions. I think they are a great feature. They're just misunderstood and maligned by Bruce Eckel who didn't get them.
But they make my code much cleaner because I use them properly.
It's fine if some languages are not as strict with types or errors than others. But that was Java's philosophy and I have no problem with it.
Putting if/else error checking is definitely a step backward.
I guess a better way to do error handling in Go would be to return a "result" object that would have an "value()" method to return the value, or an "ifError()" method to handle the error with a lambda.
But the nice thing about exceptions was being able to largely forget about them and let them trickle up to some handler. But it seems to be a hard concept for some people for some reason. Error codes aren't better.
Thanks for this article, I was reading it, as js/ts developer, who also know a thing or two about golang. (and usually dislike java).
I can't agree more about the aweful error handling. every second line checking for an error.
Once I was writing unit tests, for an existing project. I was using an sql driver mock. It feeled very cumbersome, not productive. Along the way I did some refactoring. First doing the change I wanted, then recompile and run the test as long as everything got into its place again. It was very much guided by golang. (statically types language). While I did not feel productive, only fixing what the compiler tell me, (can't the compiler fix the code for me),... but it absolutely feeld satisfying in the end. and I was a little proud when that work was done.
"Error handling makes the code less readable"
You can make it ja biut more like your example:
result1, err1 := doFirstTask()
Stacktraces are possible
I always refactor by hand because then, it comes out the way I want. (and it's not that much more effort)
I'm curious. Refactoring is a completely deterministic process that requires some effort and attention to detail but no creativity. Because of that, it's also easy to make mistakes by forgetting a detail or due to a copy/paste error.
In other words, it's a perfect task to be automated. In what way do you not get it exactly as you want it, when e.g. extracting a variable or a method?
Refactoring in my eyes is not only to move parts of a complexer function out of there, but rethink why the function was complex in the first place. This kind of refactoring cannot be automated.
I daily check my code for complexity of functions (automated) and refactor when I spot a candidate, because then, it's still fresh in my memory.
That sounds reasonable, and obviously not something a computer can do for you.
I tend to use the term 'rework' for that, while sticking to the traditional meaning for 'refactor'. But as long as all parties understand what is meant, that doesn't really matter π.
Love the Java Perspective of the Go language.
It feels like Go is the next step for low level languages (C/C++), but the creators forgot some great high level language features.
This makes Go's target applications to be command line executors and independent algorithms.
If Go had embraced some high level concepts like runtime exceptions, JPA, annotations (Spring boot), generics, we could have written any type of app we wanted, with any large scale team of numerous levels of developers.
You do realize that many of these points have nothing to do with the language itself, right?
You don't have to return errors if you don't want/need it. Second version of your function is ok too. The idiomatic handling is verbose, sure but whether or when you need it is up to you. There are plenty of cases when you wouldn't want to return anyway (eg: from goroutines)
Also, your not handling error example is a side effect of reusing variables. I advise against it on principle and I find it sad that Go allows it.
Goland has shortcomings for such an expensive product but many people prefer VSCode for that. I find significantly better than Goland IMHO. But it's weird to say Go has poor IDE support. Did you try anything not by Intellij?
Why parenthesis are not enforced around ifs? Why would they be? You need a reason to use a syntax element not to not need one. Semicolons also exist but are not enforced unless you have multiple statements on a single line.
I find the error handling tedious but not unreadable. It's actually very readable imho, more so than a catch/throw system where the actual handling may be elsewhere. I look at a file and know exactly what happens to my errors (and I can handle only what I want to handle)
Thanks, for your comment. I really appreciate it, even if you don't agree. It is always possible to learn something new.
First of all, my reception of the language is mostly positive.
I know that I can handle errors in different ways, but it will not be idiomatic, most libraries do return errors, so I will have to wrap functions from some lib into my own. So different way (i.e. using panic) is not that good too...
About not reusing variables - agree, but there are plenty of examples, questions etc. where people do reuse error variables. Maybe they are wrong, but my conclusion is that reusing error variables is idiomatic.
You are right that I haven't tried VSCode (I use it but not for Go programming), now I have. It also has shortcomings... And extracting function refactoring creates worse results than in GoLand - it does not compile.
When it comes to parenthesis, this is what I am used to and find more readable. It is not that important though.
Error handling - my opinion is that it forces you to write less readable code. Catch/throw system without checked exceptions is what I like the most.
We can disagree because we are different, have different experiences etc.
It's not that I don't agree. In fact, I'm familiar with the ideas as they're nearly the same as what I thought coming into Go from PHP. What I'm talking about is a bit of perspective on approaching a different language (and also a bit about separating what makes a language: aka the qualities of the language itself as separate from the platform, including its tooling and again separate from third party support in the form of IDEs).
Thing is, you should define what "idiomatic" means. From what I infer from conversations, it's mostly "the way of doing things that the language enforces or hints at" which is different than "how most people do it through materials/online howto's". I'm sure you also found that quick stackoverflow solutions and straight-to-the-point articles are hardly a collection of best practices. Reusing (or not) is not part of idiom, in this way. You could say that how most people approach solutions should be considered so (particularly because you'll see this in the stdlib, with the caveat that stdlib itself is a collection of tools and ready-made solutions made by people and hardly perfect) but in that case "idiomatic" loses any value since it's not a benchmark for current best practices and merely a mirror or what the majority o developers chose to do some time ago. Following the idiom in this way precludes progress and I've seen this first hand in PHP: it started as a bunch of useful scripts, then grew into a sort of procedural kind of thing before slowly developing OOP-ish ways. What's considered idiomatic has evolved, even if you'd find plenty of people nowadays that swear by procedural spaghetti code of ye olde days.
Thing is, I also liked the try/catch and lots of other stuff in PHP ... but it took learning a few languages that used different paradigms (ranging from slightly different to "other side of the world" different) to put some things into perspective and reconsider the qualities of what was familiar.
How K8s deals with no generics ... mess or not mess ... π€
The core team replaced a compile-time language feature that was missing (Generics) with their home-built runtime system. And given the tools at their disposal, they did a pretty good job.
medium.com/@arschles/go-experience...
Generics are probably going to be added to the language in the future:
github.com/golang/go/issues/43651
blog.golang.org/generics-proposal
The proposal was created on 2021-01-12.
The less parenthesis is more readable, I always find why other languages tend to use lots and Ruby syntax was cleaner. Go has a well-balanced syntaxes of all languages that it can do many area good enough.
func keyword is derived from B language by Ken Thompson.
I tried go, but a language without generics is hard to use...
I am currently transitioning from JAVA to GO. I feel the same about error handling for sure.