9 ms·
I never understood how people consider go an alternative to python. I tried it myself for a while, and I couldn't stand the verbosity. So much code needed to do
by jgb1984 4y ago
I never understood how people consider go an alternative to python. I tried it myself for a while, and I couldn't stand the verbosity. So much code needed to do simple things, it almost felt like C again. And the error handling... Shudder.
I'm very happy python survived the 2-to-3 migration and is now thriving more than ever! Looking forward to the 3.11 speed improvements.
- enriquto 4y ago> And the error handling... Shudder. As an anecdotal data point... I like Python and prefer it to Go for the most part. However, in the case of error handling, Go seems to get it right and Python does it very wrong. Exceptions are extremely confusing, I just can't wrap my head around them. I don't even accept, philosophically speaking, the concept of exception: there are no "exceptions" when running a program, only conditions that you dislike.
- scaredginger 4y agoStrongly disagree with your conclusions. There are definitely arguments against exceptions in cases where you always want to understand and handle failure conditions, but Go has not got it right. The need to manually handle returned errors adds too much noise to the code, and in my subjective opinion, the code looks far cleaner when you skip the error checking. Additionally, if the manual error handling code isn't written, failures will generally be silent. My example of error handling done right would be Rust's `Result<T, E>`. There's the ? operator for syntactic sugar to propagate errors upwards. Otherwise, to skip error handling, you're generally forced to unwrap() the result, which is a noticeable smell, and it will crash your program if the operation failed (as opposed to continuing despite the error). Finally, it maintains the error code advantage of keeping the control flow explicit and visible. We've had decades of erroneous error handling from C codebases to learn from. Frankly, it's insane that we have a modern language that still uses error codes in essentially the same way.
- grawp 4y agoThis!
- kortex 4y agoRust absolutely nails the error handling department. Python style exceptions, pros: - rapidly drop through layers when needed to get to a stable entry point - easy to add errors deep in the stack Cons: - basically a limited GOTO, therefore side-effect, therefore not composable - no way to know if what you are calling is gonna throw on you Java pros: - have to declare exception types, so at least you know what you are dealing with Cons: - you have to always declare exception types, which can be tedious - same non-composable GOTO-lite behavior - adding an exception deep in the stack is tricky Go pros: - errors are values, therefore typed and technically composable - no surprises Cons: - tedious, verbose - can't rapidly unwind unless you panic - rarely composable in practice, requires manual unwrapping in most cases - adding an exception deep in the stack is tricky Rust Result pros: - strongly, statically typed - no surprises - higly composable - can map over it - rapidly unwind in a composable way with ? operator Cons: - adding an exception deep in the stack is tricky (but often amenable to programmatic refactoring) It's no wonder Rust is the SO most loved language 7 years running. Python actually has a Result type library which I really like, but it's been hard selling my team, and you really need buy in. But I'd give it a swing. https://pypi.org/project/result/ https://pypi.org/project/result/
- valenterry 4y agoSorry, but Rust's error handling is pretty clumsy compared to what you can do in some languages. Example: You write a function that calls 2 thirdparty libraries, both of which can fail. The typesystem in Rust is unable to express that the resulting error with be libAError or libBError. It is lacking anonymous union-types. Even if union-types have been added, you'd have to define one first and you'd have to use unsafe (at least from my understanding). This also impacts user-defined error-types of course, but it makes errorhandling when using other libraries very annoying. You always have to define new types and do wrapping. Another example: You have a function that calls 2 thirdparty libraries, both of which can fail. The two libraries have decided to use a customized error-type (and not the built-in one) because they need to carry around some extra context or need some other feature, but they still support everything that Result does but in a slightly. Now you need to manually convert those or learn the interface of this specific error-type, because there is no way to abstract over different "Result"-types. Why? Because Rust has no support for Higher Kinded Types, which would be needed to do so. There are more examples, but these are two that are immediately relevant to pretty everyone using Rust. And yes, Rust has done a lot of things right and especially better than C or C++. But when looking at it as someone who has used other high-level-languages I can say it definitely did not "absolutely nail" it.
- magicalhippo 4y ago> Exceptions are extremely confusing, I just can't wrap my head around them. Interesting. For me it's very much the opposite. What about them do you find confusing? > I don't even accept, philosophically speaking, the concept of exception: there are no "exceptions" when running a program [...] I think you're looking at it wrong. Exceptions, as implemented in the languages I know, are more about having a framework of allowing inner/library code to drop the ball and run away screaming, without making more of a mess.
- enriquto 4y ago> What about them do you find confusing? They are non-local jumps. They make reasoning about the code very difficult. When you read code, you have to treat all conditions symmetrically and equally likely (e.g., whether a file exists or it doesn't). Using exceptions for control flow, as is done sometimes in python, forces an unnatural asymmetry between cases that I find confusing. And this is just when there is a single exception at stake. Typically, several exceptions fly invisibly over the same code and it becomes impossible to understand (unless you assume that no exceptions occur, which is the wrong stance to take when analyzing an exception-riddled code). TL;DR: raise and except are a corporate-friendly renaming of goto [0] and comefrom [1]. [0] https://en.wikipedia.org/wiki/Goto https://en.wikipedia.org/wiki/Goto [1] https://en.wikipedia.org/wiki/COMEFROM https://en.wikipedia.org/wiki/COMEFROM
- magicalhippo 4y ago> When you read code, you have to treat all conditions symmetrically and equally likely Pretty sure I don't do that when I read code. When I see "list.add()" I don't consider running out of memory and the operation failing equally likely to the list being added to. And if it did, in 99.99% of the cases I'm fine with it just bailing, because there's not much else to do at that point. I agree that using exceptions for what could be considered normal conditions is not great. Trying to open a file that doesn't exist isn't by itself an exceptional incident. The calling code might consider it an exception and decide to raise, but the IO library shouldn't.
- 4y ago
- int_19h 4y ago"Exception" is arguably a historical term in Python - it comes from languages where exceptions really are exceptional and very rarely handled; but when they're used for regular control flow, yeah, that's definitely a misnomer. But our vocabulary is full of such things. A "method" is not a particularly descriptive term given what we use it for these days, either. At the end of the day, so long as everybody knows what it is, it's not a big deal. Conceptually, though, it can be treated as an error monad.
- kortex 4y ago> Conceptually, though, it can be treated as an error monad. Exceptions are very much not a monad, that's one of the biggest pain points about them. You can't map over exceptions. They are a control flow statement.
- int_19h 4y agoThey're a monad with an implicit hardcoded mapping function that's basically equivalent to Rust's "try". Yes, the fact that you can't change the function is a pain, but it doesn't preclude reasoning about them in this manner.
- codyd51 4y agoThere are indeed exceptions when running a program. Exceptions, as a construct, are baked in at the hardware level. When a program page faults, or tries to divide by zero, or runs an invalid opcode, the CPU will jump to an exception handler in much the same way as the higher-level constructs in Python and elsewhere.
- the_duke 4y agoI also much prefer explicit error handling and really disklike exceptions because every function can fail in a myriad of ways and you have no idea how. Rust does it much, much better than Go though, thanks to sum types and syntax sugar for early returns (`?`). But the flipside is that encoding every failure condition in the type system quickly becomes unfeasible. Every allocation can fail. Every array/slice indexing can be out of bounds. Some errors might just not be recoverable at all while maintaining consistent state. Go has the null pointer problem... That's why both Go and Rust hide certain error conditions and have a fallback mechanism (panic/recover). There is a balance between the two idioms, and which one is right depends on the language and the facilities the type system provides.
- papito 4y agoIn most of my code, exception handling happens in one file, one function. You throw an exception, and you let it propagate until it is handled. Why is this so hard?
- robertlagrant 4y agoBecause where the code that handles it is not the problem (in fact, optimisting that for simplicity could cause other problems). The problem is what state is everything left in when the exception is thrown? What if you're halfway through writing to a file/network socket/screen/shared resource when an exception happens?
- papito 4y agoThat should be cleanly handled in a language like Python and others, where the context managers clean up the resources when exiting a scope, normally or after an exception. It's like stack unwinding is a new concept or something.
- robertlagrant 4y agoYou're missing the point. The OS resources might clean up, but that doesn't mean the application's world (some of which may be a file on a server on another continent) has been left in a known good state.
- gunapologist99 4y agoTrying to understand your point. If you try to open a file or a network connection or divide by zero, you don't handle any exception in that calling function, but instead let them propagate all the way to the top?
- papito 4y agoWhat are you going to do with that? Keep returning errors until the calling function gets the error? That's what exceptions do for you. Do you have any guarantee that there is no bug in the error-handling code somewhere? And what will be the final result of the error? An error screen, right? That's presentation layer. Any fatal error should halt execution, be thrown to the top, and manifest itself as a message in the presentation layer, right? Throwing an error is not "not handling it"!
- prirun 4y ago> there are no "exceptions" when running a program, only conditions that you dislike. Exceptions, aka faults, are a time-tested feature of CPUs that have been around for half a century: https://wiki.osdev.org/Exceptions https://wiki.osdev.org/Exceptions
- enriquto 4y agoSure, but people here are talking about exceptions as a control-flow feature of high-level programming languages like Python. It has nothing to do with cpu exceptions or interrupts.
- xtracto 4y agoI don't think that's a good argument... otherwise we would like to honor the prominence of JNZ, JNE, JL, LOOP and other "spaghetti code" constructs used deep in CPU models but that are just not appropriate for human readable code.
- talideon 4y agoGo's error handling is one of its single worst features. It would be much better if if had supported algebraic data types (which would also eliminate two other abominations: `iota` and `nil`, with the former being replaced by type constructors as sum types can act as enums and the latter would be replaced with an `Option` type) along with some kind of pattern matching. If you're not familiar with algebraic data types, they're well worth learning about, and not a difficult concept. Once you use a language with them, heading back to a language without them feels like developing with one hand tied behind your back.
- wodenokoto 4y agoI don’t know if this helps or just makes it more unacceptable to you, but exceptions in Python is more of a second channel of communication between parts of programs. Since messages in this channel propagates up the call stacks they are very handy to stop an application and therefore used for error handling. But they can just as well be used for all sorts of other messaging. In base Python they are used to communicate that you’ve reached the end of an iteration. If you’ve ever made a function that returns both a value and a status in as a tuple, chances are you are better off using exceptions to communicate the status, especially if there is a “stop” status.
- gunapologist99 4y agoThis is true, that's exactly what exceptions are. One could argue (I would) that this is a very powerful part of Python, but the indeterminate nature of the exception (where is it coming from, how is it defined, what should I provide to my client) makes it easier to catch exceptions as they percolate upward, but more difficult to correctly know how to handle them (especially as the permutations approach infinity as underlying libraries get upgraded). Golang actually has a back-channel communication path that is somewhat similar as a second channel of communications. (Actually they're called channels!) They're a first-class and extremely powerful feature of the language. You can even run a for loop over them, block or not block while waiting for an incoming message, etc. Here's a great video that talks about use cases and the patterns in using them, and they pair great with goroutines (and you don't have to sprinkle async before every function, either): https://www.youtube.com/watch?v=f6kdp27TYZs https://www.youtube.com/watch?v=f6kdp27TYZs
- usrbinbash 4y ago> I never understood how people consider go an alternative to python. I work in both Python and Go. I love both languages. Here are a few reasons why I chose Go over Python for a lot of things these days: Alot faster, self-contained executables instead of virtualenv, scales natively, no GIL, linear code in goroutines instead of asyncio.
- garyrob 4y agoI'm not associated with the nuitka folks, but I want to mention that I've successfully used it to make self-contained executables for python projects. I haven't used it for anything huge, but so far it's worked flawlessly.
- toyg 4y ago> self-contained executables This really is the only practical area where Go is superior to Python. No messing with 3rd-party libs, no compromises, no hacks... Everything else is not really critical for most people, and largely not worth losing all the nicer features of Python for; if they could just fix it once and for all in stdlib, just blessing one compilation format and making it well-supported on all the big platforms, it would be a big step forward for the ecosystem. Easier said that done, of course.
- Jcowell 4y agoAgreed. If python had a sensible self contain executable where I don’t have to fight and figure out how I’m going to share this program with it’s dependencies and imports I would use it over go in non speed needed applications.
- odiroot 4y agoSame here. This baffles me. Especially considered the obligatory compilation step and lack of IPython.
- papito 4y agoNever understood this. I've been doing coding for a quarter century. There is a reason we moved away from manual error handling to exceptions. It's easier to reason about, it's less code, it's better. The Go fans keep insisting that this error handling in Go is new and fresh. I looked at my Go code, I saw that 2/3 of it is basically checking if a function returned an error, and I moved on. I've done it this way - I am not going there again. And no, it doesn't force you to handle errors. You can simply not check for errors, just as you would swallow an exception. My impression of the Go culture is that of the hip kids discovering vinyl. As my former boss used to say - "there is an easy way, and there is the cool way".
- StevePerkins 4y agoEvery other major programming language is cargo culting as much "functional programming" as it can bolt on to its chassis. Which is then WAY overused by a small number of self-styled "10x-ers", to dump a bunch of unmaintainable write-only code into your codebase before jumping ship and abandoning its support a few months later. Go and Python code may have significant difference. But there is spiritual overlap in the sense that Go really encourages a straightforward imperative programming style, which makes its code more accessible to a wider range of developers.