Live data from Hacker News

Why I’m not leaving Python for Go

uberpython.wordpress.com

151–160 of 245 posts

Re: Why I’m not leaving Python for Go

#151
post #120

I have mixed feelings about errors as return codes. Then again, I have mixed feelings about exceptions. There are two general use cases for exceptions: 1. Unexpected (typically fatal) problems; 2. As an alternative to multiple return values. (1) is things like out of memory errors. (2) is things like you're trying to parse a user input into a number and it fails. I despise (2) for exceptions. It means writing code li…

I agree that exceptions can be cumbersome at times. And I do prefer the Go parsing float example to the try/catch one if my entire program is only one line. But in order to live in peace you have to either: * Force every programmer to always check all the error values. or * Allow errors to pass silently. So either you accept Go as a heavy-duty, heavy boilerplate error handling laden language. Or you accept it as a fl…

So, I rarely see eg Java code that actually handles the exceptions it receives. Usually its just "Oh, I got an exception, so I'll print an error (or silently fail) and blindly keep going".

Given this, I don't see how Go is any worse with respect to sloppy programmers. They'll Always Find A Way.

Also, why couldn't your second example be nested? It seems like either of those two examples could be structured identically to the other.

Re: Why I’m not leaving Python for Go

#152
post #149
post #133

Earlier quoted context omitted.

Java 7 has AutoCloseables for resource cleanup: try (FileInputStream f = new FileInputStream(path)) { // do stuff }

The python mechanism is more general, you can use the /with/ construct with locks for example.

You can do the same with Java. Both are in a sense trying to re-gain the most useful parts of RAII semantics from C++: making it much more difficult to forget to clean up resources after you've finished with them.

Re: Why I’m not leaving Python for Go

#153
post #144
post #143

Earlier quoted context omitted.

I wouldn't agree, as when I'd code it in C I'd have a function callSql which returns false and write the code you describe as: if ( callSql( s1 ) && callSql( s2 ) && callSql( s3 ) && callSql( s4 ) && callSql( s5 ) && callSql( s6 ) && callSql( s7 ) && callSql( s8 ) && callSql( s9 ) && callSql( s10 ) ) { endTransaction(); return true; } logAndAbortTransaction(); return false; No need for exceptions at all, and it's ver…

While this code is nice and concise, the problem is that you don't automatically know which call failed or what the error was.

The loop variant does know which call failed, and could easily be modified to know what the error was (if the underlying callSql had more than just a boolean failed/success return value).

Re: Why I’m not leaving Python for Go

#154
post #151

Earlier quoted context omitted.

I agree that exceptions can be cumbersome at times. And I do prefer the Go parsing float example to the try/catch one if my entire program is only one line. But in order to live in peace you have to either: * Force every programmer to always check all the error values. or * Allow errors to pass silently. So either you accept Go as a heavy-duty, heavy boilerplate error handling laden language. Or you accept it as a fl…

So, I rarely see eg Java code that actually handles the exceptions it receives. Usually its just "Oh, I got an exception, so I'll print an error (or silently fail) and blindly keep going". Given this, I don't see how Go is any worse with respect to sloppy programmers. They'll Always Find A Way. Also, why couldn't your second example be nested? It seems like either of those two examples could be structured identically…

I find Java exceptions quite good for cases where you want to fail at a much coarser level than the specific problem, but finer than the whole program. E.g. my previous^2 job was essentially a message-processing system; it had a bunch of loops that took messages off queues and processed them one at a time. If processing any given message threw an (uncaught) exception, it marked that message for retry or as failed and carried on.

You could certainly argue for handling each message in a separate process a la erlang, but this approach worked well for us.

I think checked exceptions were a mistake (IIRC Gosling agrees), and Java could do with better support for multiple return values (which exceptions get abused for), but I like Java-style runtime exceptions.

Re: Why I’m not leaving Python for Go

#155
post #19

> Verbose and repetitive error handling If you were to handle the same error cases in Python that you gave examples for in Go, it would probably be even more verbose with the try/catch wrapped around. > Errors passing silently – ticking time bombs to go Are you kidding? Unless exceptions are documented well, which they rarely are, this is a much greater problem when using exceptions.

>Are you kidding? Unless exceptions are documented well, which they rarely are, this is a much greater problem when using exceptions.

No it's not. If you get an exception you didn't prepare for, your code fails, not silently but very loudly. The argument to have is whether this is better or worse than your code silently continuing to do all the next steps even though the first one failed.

(FWIW I agree with the article, but will say it's kind of surprising to see that point of view from a python programmer given the python approach to type-checking)

Re: Why I’m not leaving Python for Go

#156
post #79
post #72

Earlier quoted context omitted.

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() } } ?

Yes. The advantage of the second form is that

  x := something()
  n := somethingElse(x)
  andSoOn()
no longer compiles.

Obviously you would also want some syntactic sugar to make it look nice, but that's quite simple.

Re: Why I’m not leaving Python for Go

#157
On the other hand, us who haved loved and embraced C don't mind this at all. It's up to us how to arrange the error handling.

Different situations warrant different strategies. Sometimes it's ok to return NULL, sometimes it's ok to return true/false and pass the actual result back indirectly, sometimes it's ok to return the value but use an external error flag. Sometimes it's ok to mix control flow and external flagging of errors, namely what an exception is, effectively.

However, in my opinion, languages who point too much to one single error handling mechanism are more irritating than languages that leave it up to the programmer. I'm particularly wary of exceptions per se: they're a really nice, clean concept but on the other hand I rarely hit an use case that would be a perfect hit for exceptions, and even those perfect cases for exception-handling wouldn't look too shabby if designed with other error handling strategies.

On the other hand, many cases where errors are handled with exceptions get awfully ugly in the normal case. For example, the "try: ... except : pass" idiom in Python. I can't count the number of times I've written a small wrapper function around a trivial exception handling case, that just flattens the result and error into a suitable value if that fits my use case.

Re: Why I’m not leaving Python for Go

#158
post #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 allocate…

>In the hands of someone who knows what's going on, programs get much simpler I never really got this argument. With RAII types that have value semantics (ala shared_ptr) what is so difficult about memory management in C++? I guess there are reference cycles, but weak_ptr can help there. Manual ref-counting (like a COM AddRef/Release pattern) can be tricky, but that is where attention to detail, code reviews and basi…

In my experience of web server apps, it's been fairly easy to guarantee that either the whole request succeeds, or - if there was an exception - every change is rolled back. Both user state and database state has been transactional, and state that lived on in memory in between requests was either read-only or caches.

In UI apps, it's much much harder to guarantee transactional semantics unless you're using persistent data structures or similar techniques that make in-memory transactions / undo trivial to make correct. So it makes more sense for exceptions there to save user data where possible, log the error (potentially back to the vendor), and restart the app.

(I don't want to talk about C++. I think both C++ exception handling and memory allocation are broken by C++'s design. You can only make it sort of work with coding standards, and even then it's labour intensive. If you don't yet understand why GC increases productivity, it's an epiphany you'll need to look forward to. Bonus: GC also makes exception safety far easier. For example, you can write a stack.pop() that returns the value popped - one of my favourite examples of C++ being broken.)

Re: Why I’m not leaving Python for Go

#159
post #120

I have mixed feelings about errors as return codes. Then again, I have mixed feelings about exceptions. There are two general use cases for exceptions: 1. Unexpected (typically fatal) problems; 2. As an alternative to multiple return values. (1) is things like out of memory errors. (2) is things like you're trying to parse a user input into a number and it fails. I despise (2) for exceptions. It means writing code li…

One problem with Go is that is uses multiple return values to indicate errors instead of alternative return values. When you call strconv.ParseFloat, you always get back two values, the error code and... wait, what float do you get back when there's an error?

If you're going to use return values to indicate errors (and certainly I feel that there are reasons to use them, exceptions vs error codes is not an either/or proposition IMO), you should use sum types and return either an error or the correct value and have a mechanism to make sure you handle both cases.

ML and Haskell get this right, it's a shame Go overlooked them in this regard.

Re: Why I’m not leaving Python for Go

#160
post #82
post #76

Earlier quoted context omitted.

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.

In Go unused variables are an error, so you are forced to either handle errors or explicitly ignore them (by assigning them to _).

Sort of, you can actually get away with invoking a function and not assigning its result at all

I think assigning the error to _ is only necassary when you have multiple return values(one of which is the error) and you are interested in at least one of them.

e.g.

    returnsTwoValues()
is legal

    x := returnsTwoValues()
is not

maps are also interesting because

    value := someMap[key]
is valid and

    value, keyIsPresent := someMap[key]
is also valid.
Post reply on HN