Live data from Hacker News

Why I’m not leaving Python for Go

uberpython.wordpress.com

71–80 of 245 posts

Re: Why I’m not leaving Python for Go

#71
Exceptions are like garbage collection.

Garbage collection relieves you from the drudgery, accounting and bureaucracy of manual memory management and indirectly leads to more expressive forms of programming once function boundaries are freed from having to specify who owns the data being passed back and forth. But you still need to be aware of space vs time usage, you still need to know where memory is being allocated, used and kept alive in your program, and you can still have "leaks" where a data structure is rooting too much dead data. This isn't immediately obvious to the novice, but that isn't IMHO a good reason to dislike the "magic" behaviour of GC. In the hands of someone who knows what's going on, programs get much simpler.

Exceptions relieve you from the drudgery, accounting and bureaucracy of manual error passing and recovery, and indirectly leads to more expressive forms of programming once function boundaries are freed from having to specify how rich error conditions are passed back along the chain of callers. But you still need to be aware of errors that require aborting and errors that can be fixed and resumed. You can still ignore exceptions that leads to programs crashing when they shouldn't. Looking at a program using exception handling, it sometimes isn't immediately obvious to the novice how the exception plumbing is working (especially since you don't usually see it at the function call level), but that isn't IMHO a good reason to dislike the "magic" behaviour of exceptions. In the hands of someone who knows what's going on, programs get much simpler.

Re: Why I’m not leaving Python for Go

#72

I'll agree that this is perhaps the single thing that I don't like about Go. Now, you can argue it helps with "explicit error handling" and all that, but in that case you might as well have must-be-caught exceptions. Anyone want to fork Go?

If anything, they should have used sum types instead of product types to handle errors.

Re: Why I’m not leaving Python for Go

#73
post #61

Earlier quoted context omitted.

"Of course, you can add more code to deal individually with each of the exceptions in more robust ways, even to the point of wrapping each statement in its own try/catch block, but then it's no different in practice from returning error codes." It is very different. With a try/catch block, you can see the normal logic flow of your code in one place, separated from the error handling logic. If you wrap each and every…

Um... errors ARE normal operation. Consult historical output from your C++ compiler, if you don't believe me! (If anything, that should probably be "errors", quotes included, because what people usually mean by the term is "easily-forseeable occurrence that I couldn't be bothered to write the code for".)

Compiler errors aren't usually implemented as exceptions because compilers these days don't stop at the first error.

Exceptions are ideal for things that require aborting and unwinding to a point - usually a loop, like a server request handler or UI event dispatcher - high up on the stack. If the general behaviour is not to abort and unwind, it's not a good fit for an exception.

It's also why exception handlers should be very rare. There shouldn't be any more than a half-dozen in most programs. Error codes that need checking and aborting by hand at every function call boundary are a poor fit for replacing exceptions, because they optimize for the case where the error can be handled, rather than the usual case for exceptions, where the error cannot be handled.

Re: Why I’m not leaving Python for Go

#74
post #64

If you use a language that uses error codes, then you're forced to explicitly think about what to do in every single error case (or not, and accept the consequences). This can add a lot of mental overhead, but can result in a much more robust and well-thought-out program. But because more logic is involved period (to handle the robustness), the program is necessarily more complex. If you use exception handling, then…

Joel Spolsky on exceptions : http://www.joelonsoftware.com/items/2003/10/13.html

> They create too many possible exit points

Irrelevant with go, thanks to the `defer` statement.

Re: Why I’m not leaving Python for Go

#75
post #28

Earlier quoted context omitted.

That's his whole point. He doesn't want to ignore errors but feels like Go is so verbose about it that it's a pain for programmers to deal with it. Example in case, print . Would you check its return value every time you call it? No. Contrast that to other languages, such as python, where you don't ignore the errors because you know that if something goes wrong, an exception will be raised. It lets you do stuff like:…

It's actually worse than that. The blog has error handling code like: if err := datastore.Get(c, key, record); err != nil { return &appError{err, "Record not found", 404} } This is pretty typical Google Go error handling, where errors are only actually used as boolean flags. Errors are universally returned as type 'error' so to actually do anything with the error you have to first cast it to some specific type. But t…

Yeah this needing to dig through the source is no end of pain, when you do actually want to handle the error.

Re: Why I’m not leaving Python for Go

#76

If you use a language that uses error codes, then you're forced to explicitly think about what to do in every single error case (or not, and accept the consequences). This can add a lot of mental overhead, but can result in a much more robust and well-thought-out program. But because more logic is involved period (to handle the robustness), the program is necessarily more complex. If you use exception handling, then…

The default behavior for an error in a programing language with exceptions is to crash. This gets your program back to a known state (i.e. not running). It's impossible for code to blindly continue when something bad happens. If you want to handle some error in a way other than crashing, handle it. Web frameworks usually catch exceptions that arise while a request is being handled, finish the request, and keep handli…

I don't know about Go, but lint is commonly used to enforce checking of return values in C. Presumably Go could bake an option into the compiler to enforce this too.

Re: Why I’m not leaving Python for Go

#77
post #28

Earlier quoted context omitted.

That's his whole point. He doesn't want to ignore errors but feels like Go is so verbose about it that it's a pain for programmers to deal with it. Example in case, print . Would you check its return value every time you call it? No. Contrast that to other languages, such as python, where you don't ignore the errors because you know that if something goes wrong, an exception will be raised. It lets you do stuff like:…

It's actually worse than that. The blog has error handling code like: if err := datastore.Get(c, key, record); err != nil { return &appError{err, "Record not found", 404} } This is pretty typical Google Go error handling, where errors are only actually used as boolean flags. Errors are universally returned as type 'error' so to actually do anything with the error you have to first cast it to some specific type. But t…

You can switch on the error type, since "error" is an interface.

    switch err.(type) {
       case Type1: 
          // do something
       case Type2:
          // something different
       default: 
          // something else
    }

Re: Why I’m not leaving Python for Go

#78
post #25

Earlier quoted context omitted.

Because hype != progress. Hype is basically a bag full of air. You can try new things, but abandon the other ones because of hype (or thinking it's better cuz "ppl who don't know anything about languages said so") instead of true technical merit is pretty fucked up :) Now then again Go ain't bad, but I don't see it being better enough than Python to switch in this case. Python in better for many things as well. Not a…

Hype is a bunch of people claiming something is good. If there are enough people, you should probably go find out whether it really is good. If it really is, then voila ! Progress happened. If it's not, you stop. This guy is busy finding out, that's all.

Fair enough, but I fear that most of the time hype comes from people who fear missing the latest boat, or who think it'll help them if they say stuff like XX is cool (a misplaced positive attitude always helps more, than misplaced criticism)

With mass globalization of the hype, we have countless examples of hype - even for programming - around things that aren't especially good.

Marketing people know that pretty well.

Re: Why I’m not leaving Python for Go

#79
post #72

I'll agree that this is perhaps the single thing that I don't like about Go. Now, you can argue it helps with "explicit error handling" and all that, but in that case you might as well have must-be-caught exceptions. Anyone want to fork Go?

If anything, they should have used sum types instead of product types to handle errors.

So instead of writing

  x, err := something()
  if err != nil {
      handleError(err)
  }
  n, err := somethingElse(x)
  if err != nil {
      handleError(err)
  }
  andSoOn()
you could write

  switch x := something() {
  case error:
     handleError(err)
  case string:
     switch n := somethingElse(x) {
       case error:
         handleError(n)
       case int:
         andSoOn()
     }
  }

?

Re: Why I’m not leaving Python for Go

#80
As usual in these threads about Go, I really wish people would consider Haskell as a nice alternative. A lot of people write Haskell off as "academic" or "impractical", which I feel is not an entirely fair assessment.

Particularly: Haskell is fast, concurrent by design (whatever that means, I'm sure Haskell is :P), typed but not cumbersome or ugly (less cumbersome and ugly than Go's types, even) and--most importantly for this article--does error handling really well.

In a certain sense, Haskell's main method for handling errors is actually similar to Go's. It returns a type corresponding to either an error or a value (this type, naturally, is called Either). This method is special, oddly enough, because it isn't special: Either is just a normal Haskell type so the error checking isn't baked into the language.

Coming from another language, you would expect to have to check your return value each time. That is, you fear your code would look like this pseudocode:

    if isError val1 
      then pass val1
      else val2 
However, this is not the case! The people implementing Either noticed that this was a very common case: most of the time, you want to propagate an error value outwards and only do any work on a valid value. So you can actually just write code like this:

    do val1 
then, if any of the values return an error, it gets propagated to the end of the code. Then, when you want to check if you actually got a valid value--maybe in some central location in your code, or wherever is convenient--you can just use a normal case analysis.

Additionally, while the errors do pass through essentially silently, the type checker does ensure you deal with them at some point before using or returning them. If you ever want to get the Int from a Either Error Int, you have to either handle the error case somehow or explicitly ignore it (e.g. with a partial pattern match). The latter option will generate a compiler warning, so you can't do it by accident without being notified.

So the mechanism is simple, but it can also stay out of your way syntactically. So what else is this good for? Well, it's just a normal data type, nothing special; you can use existing library functions with these values. For example, you can use the alternation operator to find the first non-error value:

    val1  val2  someFunction 5  ...
This is often a very useful idiom which would be harder to write with a different way of handling errors. There are more utility functions like this (e.g. optional) that let you make your intents very clear at a high level. The optional function, for example, lets you do exactly what it claims: you can mark a value as "optional", meaning that any error from it will be ignored rather than propagated.

You can also layer on this error-handling logic on other similar effects. For example, there are some types (like LogicT) that represent backtracking search. Combining error-handling with nondeterminism gives you a question: should an error cause the whole computation to fail, or only that particular branch? The beauty is that you can choose either option: if you wrap LogicT with ErrorT (this is just like Either except it can be combined with other types) an error will cause the whole computation to fail; if you wrap ErrorT with LogicT, the error will only cause the current branch to fail. This not only makes it easy to choose one or the other, but also makes it very clear which one you did choose: it's right there in the type system in a very declarative fashion.

Haskell also has a bunch of other advantages which aren't worth going into here. I think anybody looking for something like Go--a fast, high-level, concise, typed alternative to Python--should definitely consider Haskell. While it may not seem so at first, I believe that Go and Haskell are good for a very similar set of problems and so actually overlap quite a bit.

If you really dislike some particular things about Haskell, you should also consider some similar languages like OCaml and F#. I personally prefer Haskell, but there are definitely good cases to be made for either of the other two.

Post reply on HN