1. 27
  1.  

  2. 14

    It’s interesting to note that a lot of what comprises many style guides (take a look at PEP 8, for example) is made unnecessary by Go’s “gofmt” tool.

    “gofmt” is easily my favorite part about Go - it ensures a somewhat consistent style for all Go code I read, whether or not I wrote it. This makes it much easier for me to interact with others' codebases.

    1. 2

      Error handling in go is really noisy and verbose. err checking everywhere. It’s similar to Java and checking for nulls all the time. I really don’t like this style. I don’t know why go and Java have constant error checking where as in my JavaScript or Python it’s never much of an issue.

      1. 12

        Feels natural, but I come from a C background.

        1. 13

          I come from a try catch background in java and python and I welcome the error handling. I’ve been ignoring errors for far too long. It’s time for me to step up and be a responsible developer.

          1. 1

            I would argue that a language where exceptions are thrown is a much safer environment to work with than one where exceptions don’t exist: The thing is. Yes. We all should be responsible developers, and yet we all make mistakes.

            Yes. An uncaught exception will cause your program to terminate, but that’s not a bad thing. An unhandled exception is a spot where you screwed up and more often than not, your program is now in an inconsistent state and code that runs in the future and depends on the operation that just failed might also fail in inconsistent ways.

            When your program terminates, then you know for sure that no further damage will be done.

            I like that safety net. I much rather crash than destroy even more stuff as a consequence of missing checking for a possible error condition.

            Exceptions also help a lot in keeping different components of a bigger application separate as you don’t have to manually propagate errors back up the stack. And when you have a good semantic hierarchy of exception classes, you can handle a whole lot of error conditions in one fell swoop.

            In the web application I’m working for, I can throw a semantically named NotFoundException, no matter where in the framework it happens and I can be sure that this will eventually be turned into a 404 error for the client. No matter how deep down the stack I am: My method there and then failed to find something? Throw a NotFoundException and it will be dealt with.

            Especially in the cases where I really can’t do anything but show an error page, I don’t have to add error checking all the way up the stack from where I am. Error handling is constrained to two places: Where the error happens and way up there in the request handler where it’s turned into a nice error page (and a HTTP error). All the pieces in between don’t need to care and thus read much nicer.

            I think that’s very convenient and I really fail to see how this can be bad design.

            1. 1

              It’s actually pretty difficult to accidentally ignore an error condition in Go. The compiler throws out a lot of complaints. You can suppress the errors, but you have to pretty explicit about it.

              But yeah, exceptions actually would be pretty nice (as long as you are forced to explicitly catch or throw them, as in Java). I think Go uses error codes instead because it encourages you to handle errors at the source instead of waiting for it to propagate out. In my code, I usually just do something like

               err := functionThatCouldError()
               if err != nil {
                   panic(err);
               }
              

              This has basically the same effect as an exception in Java or Python. The program exits immediately with a nice stack trace. Although I guess this does require me to type 3 extra lines.

        2. 7

          Because Go doesn’t have JavaScript’s ability to attempt to coerce things into whatever-the-hell-JavaScript-feels-like-doing-right-now, or either of their dynamic type system where things can be added to other things whenever you feel like it.

          Python and JavaScript both have exceptions, and using try/catch when you’re running something that could do less than wonderful things is a good idea. Consider a case where you’re getting input from across the network, and you want to make sure something’s actually an integer. In Python you can’t just run type(variable) is int, because it came to you as a string or bytes. You need to cast it, but casting can cause a ValueError.

          Having exceptions bubble up and kill your program is typically a bad idea. You’d want to handle that, and the way to do it, in both JavaScript and Python is using a try/catch block.

          Go’s use of err everywhere is used instead of try/catch blocks. It makes you deal with errors when they happen, instead of letting them bubble up and be handled later. I think that’s nice. It’s much more explicit than a block at the end of your code that says “some sort of error could happen, and if it does, lets try something”.

          1. 4

            Yea, I definitely like the error handling better than I thought I would when I first started learning Go.

          2. 4

            Are you saying that handling any possible (or probable) error is a bad thing?

            Also, last I checked Java’s error trapping was exception-based.

            1. 4

              Because of Java’s “checked exception handling”, which I think we can probably agree at this point was a failed experiment, you end up having to explicitly wrap exceptions at the point where you call an API that generates them. At the extreme, you have things like this (testData is a string, and this is in a test case):

                  InputStream in;
                  try {
                      in = new ByteArrayInputStream(testData.getBytes("UTF-8"));
                  } catch (UnsupportedEncodingException e) {
                      throw new RuntimeException(e);
                  }
              
                  for (String line : new LineIterator(in)) {
                      p.dispatchLine(line);
                  }
              

              If it weren’t for the checked exceptions, in wouldn’t even need to be a variable; it could be inside the for-each thing. bjorn could have been saying, and correctly in my experience, that Golang’s multiple-value returns for errors result in you having to write similarly sequential code.

              However, I don’t think this is as bad in Golang as it is in Java, because the big problem with it in Java is that any new exception that can appear in some library you’re using must ripple upward through exception specifications, unless you wrap it like I did above. So a small change in one place can result in the need to make many functionally meaningless changes in many other places.

              What I think bjorn is actually saying, though, is that it’s like how in poorly written Java you have to check for null all over the place, because lots of things that are supposedly a value of some type are instead null, especially failure return values. I think this is probably true of poorly written Golang, too.

              1. 4

                I’ve always wondered why Java’s checked exceptions get such a bad name and this is a great example, thanks.

                I never understood it because I spend a lot of time programming Haskell where the convention is heavily in favor of various kinds of “checked exceptions” in that it’s very often impossible to use values which were created in failing contexts without either bubbling failures upward or handling them.

                The difference is that Haskell has Functor/Applicative/Monad interfaces which allow you to compose failing contexts and the values within in much more sensible and syntactically light manners. Oftentimes it’s not terrifically difficult to “check” all of your exceptions upon calling an API since you just want to glom them all together and float that new failing context upward—and that’s just (<*>).

                I don’t think Haskell’s notion of errors is completely successful. At the very least there’s a lack of strong cultural conventions which causes a proliferation of error types. Successful error handling thus becomes a small mire of mapping between various kinds of error types (Maybe, Either e for various e’s). The exception system provides slightly more strength of convention, but it’s broadly frowned upon since you now have to suspect every line of code for failures again.

                Anyway, that’s my two cents and a bit of an epiphany thanks to your code snippet here. I’d encourage anyone interested in learning more about Haskell’s error types to look at the errors package [0] which has a large number of “mapping” and “code pattern” combinators for error handling.

                [0] http://hackage.haskell.org/package/errors

                1. 1

                  I’m glad it’s a helpful example! But I think it’s kind of an extreme case. Most of the time the code needed to cope with Java exceptions isn’t this bad. (For example, most exceptions you have to handle can actually arise, unlike this case.)

                  I haven’t tried Haskell, but it sounds quite a bit more pleasant. Thank you for the reference!

              2. 1

                Yeah, this is the answer. The alternative is to just error out whenever an exception happens (which is more or less what Python does). That might be OK for a simple script. It’d be pretty disastrous if you are writing a long-running server.

                1. 3

                  In Python your server’s top-level loop can catch and log arbitrary exceptions, so that’s not really a huge problem with Python. The deeper problem, I think, is that exceptions can’t convert failure into success; they just let you determine what kind of failure you want to have. But in a language with exceptions, nearly every line of code can fail. It’s just implicit. (In a language with operator overloading and run-time polymorphism, like Python, essentially every line can fail.)

                  Writing code that handles, and especially prevents, every possible error requires a different mentality. You can do it in Python, to some extent (though running out of memory is pretty hard to guard against) but the fact that all those possible errors are invisible in the source code doesn’t help at all. If instead you have to write extra code for each thing that can fail, then you know what those things are, and you also have an incentive to minimize them — to make parts of your code that simply can’t fail.

              3. 4

                Sorry to bring up a meta discussion, but why are people downvoting bjorn’s comment?

                Error codes vs exceptions is an ongoing debate and everyone has their own reasons for liking one over the other.

                Go’s error handling is verbose.

                0 points.

                I’m used to error codes from C.

                11 points.

                Why are people downvoting an opinion they don’t agree with?

                1. 3

                  Because that’s not all of bjorn’s comment. bjorn’s comment mentions that Python and JavaScript don’t have as verbose error handling, but that’s mostly because there isn’t compile time checking to make sure you don’t do anything that could cause an exception without dealing with it somehow.

                  You should still deal with the exceptions in Python and JavaScript, but because there’s no note in the code itself that says “this might cause some problems” you aren’t typically away that those could be problems until they become problems.


                  That said, I didn’t downvote bjorn’s comment, I just think bjorn’s opinion isn’t well formed, or is ignoring why Java and Go deal with errors the way they do.

                2. 2

                  I have been using go for a while now, and I actually don’t mind the error checking. I am used to explicit-ness in other things from my Python days, so maybe that is why it doesn’t bother me, but I actually enjoy being able to handle the error where it happens

                  1. 2

                    I spend 40+ hours a week in Python land, and I generally enjoy it very much. While I don’t mind Python-style exceptions when I’m responsible for most or all of the code I’m working on or maintaining, often it’s hard to predict what your various dependencies are going to throw at you. With Go, error handling is so explicit that you are more or less forced to know what’s going on, which I like.

                    Go/C-style error handling is a lot more verbose, but it’s refreshing being 100% aware of the error cases and what is going to potentially be sent your way. With Python, you don’t know unless the documentation, unit tests, and the organization of the source code is good.

                  2. 1

                    Nice formalization; golint will catch some of these, such as structure of attached comments.