TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Why I’m not leaving Python for Go

225 pointsby ubershmekelover 12 years ago

31 comments

tptacekover 12 years ago
I'm not in love with this aspect of Go either, and I also find that the idiom for dealing with it (multiple return values and multi-statement if conditional clauses) doesn't play well with Go's scoping rules, so that I find myself regularly having to decide between cleaner conditional or an explicit variable declaration. I also don't love how it makes my code look like my teenage-years C code.<p>But I also think this is a very silly reason to adopt or not adopt a tool. No other approach to error handling is less fraught.<p>If Python is your only language, Go or something like Go is probably a very useful thing to have in your back pocket: compiled native binaries, fine-grained control over memory layout, and a simple and effective concurrency model is a good thing to have in one package.<p>There are a lot of things that annoy me about Go (and for that matter Python, which irritates me for very similar reasons). What I tell myself to get over that and keep an open mind is, Go may not be my idea of an elegant language (yet; I'm still learning to appreciate it), but it is an excellent tool. I can get over the language stuff if the tool works well enough, and Go seems to for me.<p>Incidentally, who "leaves" a language? I have a very hard time seeing how, even from the label on the tin, anyone could believe Go is a great solution for <i>every</i> problem. Python sure as hell isn't either.
评论 #4562985 未加载
评论 #4562746 未加载
评论 #4563479 未加载
评论 #4563221 未加载
评论 #4564652 未加载
cletusover 12 years ago
I have mixed feelings about errors as return codes. Then again, I have mixed feelings about exceptions.<p>There are two general use cases for exceptions:<p>1. Unexpected (typically fatal) problems;<p>2. As an alternative to multiple return values.<p>(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 like:<p><pre><code> try: f = float(someText) catch ValueError: # I just parsed you, this is crazy, # here's an exception, throw it maybe? </code></pre> where this gets particularly irritating is when you start writing code like this:<p><pre><code> try: doSomething() catch ValueError: pass </code></pre> I nearly always end up writing wrapper functions around that crap.<p>Java is worse for this because some libraries (including standard ones) abuse checked exceptions for this. I actually prefer:<p><pre><code> if f, err := strconv.ParseFloat(text); err != nil { // do something } </code></pre> or even:<p><pre><code> f, _ := strconv.ParseFloat(text); </code></pre> for this kind of scenario.<p>For the truly bad--typically fatal--error conditions and cleanup, IMHO defer/panic actually works quite well. I certainly prefer this:<p><pre><code> f := File.Open('foo') defer f.Close() // do stuff </code></pre> to:<p><pre><code> try: f = open('foo') # do stuff finally: if f: f.close() </code></pre> as Go puts the two relevant things together.<p>Don't get me wrong: I like Python too but I do think Go has a lot going for it and has a bright future.
评论 #4563946 未加载
评论 #4563750 未加载
评论 #4563595 未加载
评论 #4563645 未加载
评论 #4564104 未加载
评论 #4563593 未加载
crazygringoover 12 years ago
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.<p>If you use exception handling, then suddenly catching errors becomes much easier, and writing code is faster, but it's not like your program is any more robust. If you catch an error up at the top, your program or function is probably just going to quit. 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.<p>In the end, I just don't see much of a practical difference. In both cases, if you want to be a lazy or rapid programmer, errors will lead to your software just not working. And if you want to be conscientious about your errors, then you can do that in both cases.<p>Exception handling lets you handle errors "at a distance". It's convenient, but harder to keep track of. Returning errors keeps your code execution where you can see it, and you always know where it is. It's simpler, but more verbose.
评论 #4562644 未加载
评论 #4563255 未加载
评论 #4562685 未加载
评论 #4562654 未加载
评论 #4562753 未加载
评论 #4563114 未加载
评论 #4562584 未加载
评论 #4562738 未加载
评论 #4562624 未加载
评论 #4563067 未加载
ChuckMcMover 12 years ago
Heh, I thought the answer was "Because my blog is named uberpython and it would be way confusing." :-)<p>But error handling is one of those topics that really gets people going. From ABEND in the old IBM batch days, to uncatchable signals like SEGV in unix and uncaught exceptions in C++ or Java.<p>I tend to come down on the "decide what you are going to do in the code, right where the error occurs" flavor of the argument. So checking for errors when they occur is important to me, what to do next gets to be sticky.<p>So there are three things you want to do:<p>1) Easy - you just want to quit/die/exit pretend you never existed. This is pretty easy and most OS'es and embedded toolkits have something along the lines of 'exit' or 'exit_with_dump.' So that later you can try to reconstruct what happened.<p>2) Is "this is bad, but it might be ok later" where you want to unwind to the point where this started but not completely exit. Exceptions are pretty good for this if used well since you can catch them at the 'unwind' point and if you can attach the unwinding actions (closing files, freeing memory, etc) to the return stack then it can be managable.<p>3) You want to plod along in a degraded mode, which means you need some way of communicating with the rest of your code that you're damaged goods at some level.<p>Go has an interesting mix which I haven't used extensively but I wouldn't dismiss it out of hand. The folks writing it run services that continue through partial outages so if it were too egregious folks would rebel inside of Google.
评论 #4562567 未加载
评论 #4563567 未加载
评论 #4562565 未加载
评论 #4562557 未加载
Jabblesover 12 years ago
It's not "tempting to ignore errors". You're the programmer, if you want to ignore it, do so. I don't know why you'd want to ignore the returned value and possible error on a GET request - given that 1) it can fail for so many reasons, and 2) the suggestion that you might be "GETTING" something. Ignoring the error of a print statement is more acceptable since it's unlikely to occur and there's rarely a lot you can do about it anyway.<p>Error handling is explicit, like the quote you quoted said it should be. If you ignore the returns (either explicitly or implicitly) you pay the price in debugging.<p>"leaving me confused as to which error happens when" Panics happen when something very bad happens. You shouldn't be accessing off the end of an array, it signals that you've probably programmed it incorrectly, rather than it being a problem with your input (~ish). Same with divide-by-zero and type assertions. However, they provide another control path that in a limited number of situations, can give elegant results.
评论 #4562498 未加载
zikover 12 years ago
I suppose it's all a matter of taste. The error handling design decision is one thing I really like about Go. I write high reliability embedded software and "throw/catch" just doesn't cut it for me since it obscures possible error conditions thrown from subroutines. I want to have to explicitly deal with those errors whenever they might occur and error returns are a good way of doing that.<p>For every language feature someone likes there's someone else who hates it, and vice-versa.
评论 #4562545 未加载
评论 #4562438 未加载
elimisteveover 12 years ago
I write Python and Go code every week, and have since I started using Go almost 2 years ago.<p>I appreciate Go because I'm working hard to become an engineer who builds robust software rather than a sloppy hacker that throws scripts together. One significant difference between the former and the latter is carefully handling errors versus not.<p>Python's error handling seems much more succinct only because most of us don't both handling errors at all! Every other line throws many exceptions, but we ignore this for convenience. (Those ugly "except ___:" statements ruin our oh-so-cool one-liners!)<p>Bottom line:<p>When I feel like having fun making something simple and getting it done _fast_, I use Python.<p>When I feel like building something that _needs_ to work -- especially anything that does more than one thing at a time, or should use all cores efficiently -- I use Go. And yes, that means taking error handling seriously.
bproctorover 12 years ago
I don't understand why anyone would "leave" a language that was working well for them just because of some hype. Don't get me wrong, I think we should all be striving to learn new things, but that doesn't mean we should just drop everything to jump on the next bandwagon.
评论 #4562394 未加载
评论 #4562373 未加载
评论 #4562385 未加载
barrkelover 12 years ago
Exceptions are like garbage collection.<p>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.<p>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.
评论 #4563043 未加载
rachelbythebayover 12 years ago
Rejecting something because it's "from the 1970s" seems like a particularly weak way to roll. Automatically equating "old" with "broken" is just as lazy as automatically equating "new" with "pointless".<p>The world is far more nuanced than that.
评论 #4568486 未加载
cyberroadieover 12 years ago
The author of the blog post is overlooking a major feature here. Go does multiple return types, so you can do things like this: i, err := getValue() however if you want to ignore a return variable or in this case the returned error you use an underscore like this (same as in haskell): i, _ := getValue()<p>This is Go's mechanism for handling (or ignoring errors) which imho is really nice because you don't have to learn anything extra, like throwing/catching error mechanisms
评论 #4562447 未加载
james4kover 12 years ago
&#62; Verbose and repetitive error handling<p>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.<p>&#62; Errors passing silently – ticking time bombs to go<p>Are you kidding? Unless exceptions are documented well, which they rarely are, this is a much greater problem when using exceptions.
评论 #4563909 未加载
评论 #4562458 未加载
tikhonjover 12 years ago
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.<p>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.<p>In a certain sense, Haskell's main method for handling errors is actually similar to Go's. It returns a type corresponding to <i>either</i> an error or a value (this type, naturally, is called Either). This method is special, oddly enough, because it <i>isn't</i> special: Either is just a normal Haskell type so the error checking isn't baked into the language.<p>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:<p><pre><code> if isError val1 then pass val1 else val2 &#60;- someFunction val1 if isError val2 ... </code></pre> However, this is not the case! The people implementing Either noticed that this was a very common case: <i>most</i> 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:<p><pre><code> do val1 &#60;- someValue val2 &#60;- someFunction val1 val3 &#60;- someFunction val2 someFunction val3 </code></pre> then, if <i>any</i> 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.<p>Additionally, while the errors <i>do</i> 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.<p>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 &#60;|&#62; to find the first non-error value:<p><pre><code> val1 &#60;|&#62; val2 &#60;|&#62; someFunction 5 &#60;|&#62; ... </code></pre> 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.<p>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 <i>whole</i> computation to fail, or only that particular branch? The beauty is that you can choose <i>either</i> 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 <i>whole</i> 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 <i>did</i> choose: it's right there in the type system in a very declarative fashion.<p>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 <i>definitely</i> 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.<p>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.
评论 #4563370 未加载
评论 #4564333 未加载
评论 #4564687 未加载
评论 #4563614 未加载
mseepgoodover 12 years ago
Of course there is a difference between an "index out of bounds" error and a network connection error: one is a programming error and the other is not.
yasonover 12 years ago
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.<p>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.<p>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.<p>On the other hand, many cases where errors are handled with exceptions get awfully ugly in the normal case. For example, the "try: ... except &#60;name-of-error&#62;: 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.
belornover 12 years ago
When dealing with C and error handling, I have rather simple rule. If in the process of writing a simple program (+1k lines), and you did not find a bug in the libc documentation, then you are not looking hard enough at return values.<p>I do not have the same rule for python.
chrismorganover 12 years ago
I see both pros and cons in the Go approach.<p>I find Python's exceptions model to be something that <i>does</i> occasionally frustrate me. For example, I'm used to using NSIS for some things (the PortableApps.com Launcher being one thing); its model is that a command may set the global error flag. This means that some sorts of things will fail silently - occasionally bad, but generally good. Some styles of Python scripts (generally automation scripts rather than full-blown programs) would do much better ignoring most errors. When you have a script merrily chugging along and all of a sudden due to some obscure corner case (perhaps not even documented) in a small, incidental part of the script the <i>whole thing</i> quits, it can be rather annoying.<p>The Go approach lets you catch errors if you want, or drop them, much more easily than Python approach. My inclination at present is that the Go approach is more likely to end up with error-resilient code, as it encourages dealing with errors each time, while Python typically encourages you to let the caller deal with an exception if you can't - but often I think the caller doesn't realise that he should. Or after a couple of levels of indirection, it's not even documented that that exception can occur. (This sort of thing, I believe, is something that Java's checked exceptions were supposed to help with - either deal with the exception or declare it.)<p>For myself, I've had lots of experience with Python and am now dabbling with Go, using it for two smaller projects to decide which to use for my next major project. At the very least, I think the approach is worthwhile trying in a serious project that its merits and those of the Python approach may be more completely analysed.
TazeTSchnitzelover 12 years ago
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.<p>Anyone want to fork Go?
评论 #4562357 未加载
评论 #4562404 未加载
评论 #4562366 未加载
评论 #4562867 未加载
agentSover 12 years ago
Maybe its just me, but I like the fact that adding exponential backoff to an HTTP request goes from <a href="http://play.golang.org/p/8WQ8XXrKw0" rel="nofollow">http://play.golang.org/p/8WQ8XXrKw0</a> to <a href="http://play.golang.org/p/3tCiNhAfo3" rel="nofollow">http://play.golang.org/p/3tCiNhAfo3</a>, in no small part due to the explicit error-handling style of Go.
noah256over 12 years ago
Psh, obviously trigger_doomsday_device will only respond to an http POST.<p>Once again the president has been saved, thanks to HTTP Pedantry.
评论 #4562531 未加载
ericbbover 12 years ago
On the topic of when to use error-returns vs. when to use panic:<p>I struggled with this aspect of Go coding too and what I decided was that, in order to choose your approach to errors, you should think about how important it is that your function can be composed into an expression:<p><pre><code> x := foo(a) + bar(b) </code></pre> vs.<p><pre><code> c, err := foo(a) if err != nil { ... } d, err := bar(b) if err != nil { ... } x := c + d </code></pre> It's a trade-off. By using "panic", you enable more concise and compositional code. By using error-returns, you are being more explicit and making it easier for the caller to handle errors at the call-site.<p>Usually, functions that can fail are not the kind you desperately want to use in expressions anyway. So error-returns are more common. But there are many situations where errors can happen and where composition is a big win. In those cases, you reach for "panic".<p>If you look at some of the built-ins that can panic--I'm thinking of array-indexing and division and regexp.MustCompile(), for example--you notice that these would become very cumbersome if they used error-returns instead.<p>There's also the issue of initialization. That's the justification given in the case of regexp.MustCompile(). An initializer must be a single expression.<p>The words "panic" and "error" probably lead people to think that you should make the decision based on severity but I don't think that's right.<p>(That's just my take on it. I'm no Go expert so there are likely better ways to think about it.)
评论 #4562770 未加载
knodiover 12 years ago
This kind of error handling is the best part of Go.
pavankyover 12 years ago
&#62;That problem is errors are handled in return values. 70′s style.<p>&#62;This is one of the things I can’t stand in C.<p>Having to check error values for every function call is indeed a pain. But C has macros and I think it is a nice and elegant way to handle errors. I personally prefer MACROS to exceptions when writing C/C++ code. But to each his own.<p>Does anyone know if go supports macros ? If they do not, it is one ugly problem to have!<p><i></i>EDIT<i></i> I just realized you can write custom exceptions that can provide similar information about line numbers, functions etc. So removed a line saying that was a plus for macros.
dvhhover 12 years ago
I am eternally asking myself how do people handle exception in multi-threading programs ? Not that error code solve the issue at all, but it look easier (less complex) to handle thread error with error code
评论 #4563001 未加载
评论 #4563828 未加载
jorgemover 12 years ago
Out of curiousity: Aside from "returning errors" and "throwing exceptions", is there any interesting research into other ways of error handling?
评论 #4563017 未加载
jonmrodriguezover 12 years ago
Someone can fix this by creating and popularizing a simple preprocessor that implements a macro called "safely" that expands to something like "call this function, and then if it errors, report the error to STDERR and abort execution". Then just prefix the majority of your commands with "safely", and implement your own error-handling in the few cases where you care.
Tichyover 12 years ago
What happens in Go if there is an exception (division by zero or whatnot)? Does it just exit, no stack trace? I am so used to exceptions that I don't even know how it used to work without them. I think getting a stack trace for debugging is really important...
评论 #4564611 未加载
hiphopopotamusover 12 years ago
Personally I think the community might be spending too much time on Python 3.<p>Python 2 is fine, and the upgrade will offer few significant advantages for most people.<p>I think the effort would be better spent working on things like frameworks and tools for web development.
exabrialover 12 years ago
My opinion: I'm not a fan of Python.<p>* It's really slow. * Most people write their code statically, but it's dynamically typed language anyway. * Things like '__init__'... what? * Having something you can see (whitespace) be significant is a bad idea. * Even worse, the community likes to use spaces instead of tabs, which makes the 'significant whitespace' a significant problem. * With the exception of Flask, important design patterns and lessons: DRY, SOC, DI, SRP, etc are largely ignored and called "Java-like."<p>Anyway, that's my $0.02
评论 #4564038 未加载
dschiptsovover 12 years ago
The principle behind Go's design decision is based on actual experience and need to be understood by amateurs.<p>The idea is quite simple: <i>errors are not some rare special cases, they are ordinary, general events</i>.<p>That means there are no need to some special mechanism for handling them. They should be handled as ordinary events within common FSM.<p>There is no contradiction in using the general mechanism of examining returned values to determine the state of procedure execution.<p>Any FSM requires <i>if</i> (or <i>cond</i>) statements. It is the essence of a program's logic.<p>The mechanism of exceptions was wrong. It is wrong in Java, it is wrong in C++. It is just a bad design.<p>The good design is using the same mechanism the underlying OS uses. In case of UNIX-derivatives it is the explicit checking of a return value. Because errors are common and ordinary events.<p>Amateurs believe that errors are rare, and they will avoid them. This is over-optimistic - in practice errors are of nothing special. It is just an alternative branch of a condition, where a consequent assumed to be a success.
hasenjover 12 years ago
The error handling style is certainly different - but it's not so bad. It's a matter of style and getting used to stuff.<p>Maybe a better error handling mechanism is needed - but I'd prefer if it's not exception handling. Perhaps lisp style error handling? What ever that is - for I'm not familiar with it - but I keep hearing lispers brag about how awesome it is!
评论 #4562461 未加载