12 ms·
It seems like golang designers are totally isolated from what's been happening in the last 30 years in languages design. They still insist on their weird way of
by trpc 7y ago
It seems like golang designers are totally isolated from what's been happening in the last 30 years in languages design. They still insist on their weird way of error handling just like they were stubborn for years and years on the lack of package management and eventually a very weird and rudimentary way of it. It's sad because I use this language extensively but its weirdly mediocre design is totally unfathomable. It's like they are very stubborn to do anything but the right thing.
- icholy 7y ago> I use this language extensively but its weirdly mediocre design is totally unfathomable. So then why do you use it extensively?
- trpc 7y agobecause I use it in my day job, not by choice.
- pkaye 7y agoSo what language do you think does everything the right way?
- deleted 7y ago[deleted]
- apta 7y agoThat's a stawman fallacy. There is no perfect language. However, there are languages that almost are strictly superior to others. Java and C# in this case are almost strictly superior to golang in almost every front.
- AsyncAwait 7y agoWell, unless you consider Go's concurrency model superior, as well as the fact that you get native binaries.
- apta 7y agoCompiling to a native binary is not a strict advantage, there are many advantages for running in a managed environment, and it can be argued that for critical systems it is the superior route in fact (given that you have the resources to run said VMs, which unless you're doing embedded or resource constrained systems, is a non-issue). That being said, both Java and C# can compile to native binaries. Java is getting Fibers, basically JVM managed lightweight threads, and has a strictly superior concurrency library in the form of `java.util.concurrent`. Not to mention libraries like Vertx and Akka for concurrency and actor systems.
- Thaxll 7y ago- simplicity, never worked on those projects using layers of layers with a lot of magic ( like Spring ) - memory consumption, never wondered why you never see Kubernetes sidecar / daemonset in Java / C#? because they use 5-10x the Go memory, no thank you using -xmx -xms 512MB for a simple API server. - the billions GC settings that you need to try to make something work at scale ( hello Elasticsearch ) - Don't need 200MB of library to open a file or create a REST server - maven / graddle build system that are completely bloated, in Go if you have your vendor folder checked in ( and you should ) you just do go build . and you have your single binary - 50 line stack trace that tells nothing - observability imo is better in Go, it's getting better with Oracle adding stuff into OpenJDK, but it was a pain before without paying ( jvisual vm, mission control ect .. ) I worked with Java for many years and I can tell you that Go is a breeze of fresh air, it's not perfect but it's good for what it was designed.
- apta 7y ago> - simplicity, never worked on those projects using layers of layers with a lot of magic ( like Spring ) Another strawman. There are many other frameworks other than Spring that are simple and performant and easy to deal with. > - memory consumption, never wondered why you never see Kubernetes sidecar / daemonset in Java / C#? because they use 5-10x the Go memory, no thank you using -xmx -xms 512MB for a simple API server. No one is requiring you to use -Xmx 512MB. The JVM will take up as much memory as you give it. And newer GCs will release unnused memory back to the OS much more aggressively. > - the billions GC settings that you need to try to make something work at scale ( hello Elasticsearch ) golang barely has any settings, which completely limits how it can be used. If you want to tune your code for throughput instead of latency, well you can't. The JVM gives you this option, and even moreso with ZGC and Shanendoah (TBs of heaps with ~1ms max latency). golang's gc can't even approach that. > - Don't need 200MB of library to open a file or create a REST server That's quite ridiculous. You don't even need any dependencies to open a file. And not all JVM web frameworks are Spring. There are many light weight alternatives. > - maven / graddle build system that are completely bloated, in Go if you have your vendor folder checked in ( and you should ) you just do go build . and you have your single binary It depends on how you configure them. golang's dependency management is non-existent, so no real comparison there. > - 50 line stack trace that tells nothing On the contrary, this is one of the biggest advantages of using the JVM or .NET. You get a stack trace telling you exactly where things happened. Compare that to golang where you get a single string and you have no idea which code path was taken to reach that error. > - observability imo is better in Go, it's getting better with Oracle adding stuff into OpenJDK, but it was a pain before without paying ( jvisual vm, mission control ect .. ) The JVM is the most superior platform for metrics, measurement, and monitoring. This is an established fact, and anyone claiming otherwise shows they don't have experience in this area.
- andyfleming 7y agoGo also has other attributes that are reasons to consider it outside of language syntax.
- whateveracct 7y agoI mostly write Haskell professionally, but I still reach for Go depending on the project. Sometimes it just has the libraries I need and I can tell up front that I'm willing to deal with the tedium the language forces on me. Cost/benefit analysis.
- fxfan 7y agoI've heard great points and experienced myself how utterly legible go is. But nobody on earth has ever praised anything about the language design itself. I wonder if go came from somewhere not google- how would it fare.
- icholy 7y agoDart also came from Google. Why isn't everyone jumping on that train?
- skybrian 7y agoIt's kind of irrelevant since it's a different team with a different software niche and design aesthetic, but since you ask: Dart struggled for years to find a killer app, though Flutter now seems to be a good bet. Originally the idea was to replace JavaScript and everyone except TypeScript pretty much failed at that. (I mean failed from a popularity point of view, they often succeeded technically.) I'll also point out that coming from Google is kind of a mixed blessing these days, from a popularity standpoint.
- ChrisSD 7y agoIt should be noted that TypeScript succeed by augmenting Javascript, instead of strictly replacing it as most other languages tried to do.
- petre 7y agoDunno. It's a good language, better designed than JavaScript anyway. Maybe Flutter will change that.
- deleted 7y ago[deleted]
- aikah 7y agoDart was born "for the wrong reasons" AKA replacing Javascript, it failed at it. The language itself is quite good, better than Go IMHO, but Go has the advantage of not requiring a separate virtual machine. The problem with languages is whether they get enough momentum so that a community can be built around them. A language without an extensive ecosystem is nothing. Also since Go is "hypocritically Object Oriented", people can claim it's not an object oriented language and kind of write Go like classic structured programming such as C. But Go is OO. You can't write go without using interfaces for I/O.
- _ph_ 7y agoAnd I am so happy that they won't do anything but the right thing. Over the decades as a programmer I have seen a lot of language come and go. A lot of languages suffer from "features". Where either a language is designed with advanced concepts getting either directly in the way of writing simple programs, or even worse, getting bolted on later on, creating something very different from the initial language. I love the dependable simplicity of Go. It contains everything I need for a surprisingly large part of my work and not offering more than that is an asset. Few things in Go feel like they had been thrown in without proper consideration.
- richardwhiuk 7y ago> And I am so happy that they won't do anything but the right thing. That's clearly not true - they've made plenty of mistakes.
- jagger27 7y agoThe only big one that comes to mind is GOPATH. What are you thinking of?
- _ph_ 7y agoBesides never being an issue for me, GOPATH isn't strictly speaking a langauge issue, but an issue of the build environment, which will no longer be the default with Go 1.13. What is your issue with GOPATH, that you call it a big one?
- int_19h 7y agoNo immutability. Nullability by default, and weird corner cases like nil slices and interfaces. Language semantics requiring weird stack discipline that is incompatible with normal ABIs, and causes overhead when you need to interop. But frankly, just the design of append() alone should be a warning sign. If it were a low-level facility, fine, it's only needed to be understood by whoever's writing a library to wrap it for general consumption. But it's the idiomatic Go way of maintaining a sequential mutable collection! How is such a basic operation is so non-obvious that people still write articles to explain it, and so verbose that you must reference the collection twice to make it work right? (Indeed, the language itself kinda acknowledges how clumsy it is, by making it an error to ignore the return value of append(). But, as usual with Go, it's hardcoded - i.e. you can't do the same for your own function. And IIRC, it wasn't there from the get go, but got added somewhere along the line.)
- cle 7y agoCan you elaborate on the major developments in error handling in the last 30 years?
- tylerhou 7y agoI assume exceptions, the Error/Either monad, and StatusOr.
- cle 7y agoGo's error handling is essentially the same as Error/Either/StatusOr (returning errors as values). Re: exceptions, there are a lot of people, myself included, who do not view exceptions as "advancements", but as setbacks. Magically and suddenly subverting the normal control flow and unwinding the stack in highly concurrent programs (as is expected in Go) is an awful way to do error handling, and you end up having to bypass that mechanism and pass the errors as values across call stacks anyway, so what are you really gaining for all the extra complexity, "implicitness", and cognitive load of doing that?
- apta 7y ago> Go's error handling is essentially the same as Error/Either/StatusOr (returning errors as values). Except that you cannot easily chain calls that return errors, or it isn't really that hard to accidentally ignore errors because of shadowing or overwriting the variable you're storing your errors in. Or the fact that using a union type to represent errors is a strictly superior way, both in terms of usability, as well as correctness. People should just face the fact that returning errors as a product type is a mistake. > Magically and suddenly subverting the normal control flow and unwinding the stack in highly concurrent programs (as is expected in Go)... You may want to see what approaches Akka or Erlang takes here. golang authors just decided to ignore established practices and use clunky approaches to problems that have already been solved.
- cle 7y ago> Except that you cannot easily chain calls that return errors, or it isn't really that hard to accidentally ignore errors because of shadowing or overwriting the variable you're storing your errors in. Or the fact that using a union type to represent errors is a strictly superior way, both in terms of usability, as well as correctness. People should just face the fact that returning errors as a product type is a mistake. These are still essentially the same. Errors are returned as values, with no major difference in runtime semantics. Whether the language supports union types is orthogonal to that. > You may want to see what approaches Akka or Erlang takes here. golang authors just decided to ignore established practices and use clunky approaches to problems that have already been solved. There's not one solution, there are multiple solutions with various tradeoffs. The tradeoffs Go made are congruent with its tenets as a language that values simplicity and low cognitive overhead.
- grey-area 7y agoSo what would you do then? If you don't like it, don't present pale echoes of criticisms we've already seen (not listening to 30 years of language design...), tell us what you would do instead, because the answer is non-obvious and there is no one 'right thing'.
- alexbanks 7y agoWhat a ridiculous way to think about language design. The users of a language are best suited to define what parts of the language do not feel good to them - why are they expected to come up with solutions for the language designers? In your mind, are you only allowed to present criticism if you're also able to define solutions? Doesn't that limit the people that are allowed to criticize to the language designers themselves? That seems like an excellent way to get tools nobody wanted, no?
- grey-area 7y agoLanguage design has nothing to do with discussions on HN (at least I hope not!). It’s very easy and not very useful to come up with vague criticisms based on generalities, it’s much harder, but infinitely more fertile and interesting, to come up with a coherent thesis as to what should be done. The OP posited a right way, I’m curious as to which of the right ways they mean.
- RHSeeger 7y agoI would say that design has a lot to do with discussion.
- grey-area 7y agoThere are different kinds of discussion, I don't think a discussion site is the right forum for design, do you? Also discussion requires some content, not empty complaints without any backup. For example propose a different mechanism for reporting errors, like optional types or exceptions (both have potential problems). The OP would be more interesting if it actually tried to engage with the problem.
- jrockway 7y agoMy takeaway from the last 30 years in computer science is that errors are not exceptional. They occur often and should be accounted for near the code that generates the errors. Exceptions and return values are both sub-optimal. Exceptions encourage drastic actions for non-drastic events (exit the program if an HTTP server is transitently slow). Return values encourage ignoring the error value, and then wondering why your program broke. Special types that wrap error values or exceptions cause the same problem; when you want to defer something, your code becomes contaminated with the error type (f(x) returns an error, g(x) calls f(x) but doesn't feel like dealing with the erro, so now g(x) returns an error... and all the way it goes up to the top level.) Overall, I don't see a grand unified solution to this problem. We should make it possible for functions to declare everything that goes wrong so that recovery can be more easily tested. No language does this; they often merge vastly different errors into the same type, so the programmer is powerless to understand the possibilities. Consider two database errors; "syntax error" and "transaction aborted, retry it". Typed error systems typically condense that to a "database error", but how your program should handle the two cases are vastly different. Anyway, I'm happy with the way go works. If I don't explicitly know how to handle an error, I wrap it with a tag and return it. When I look at the alert / error logs, I know which codepath caused the problem and can investigate. For cases where I know how to handle an error, I can explicitly deal with it (yes, often with strings.HasSuffix to find the one I know how to handle). That is all I really need. If it were an exception, the code would be basically the same. So I think it's a red herring to complain about values vs. exceptions. Neither system prevents you or encourages you to write correct, robust code. If we want to do that, we need completely new tools.
- autarch 7y agoRust's type system + the failure crate (https://crates.io/crates/failure https://crates.io/crates/failure) is the nicest I've seen. It's similar to Haskell in that errors are part of the return value of a function, and the type system enforces handling of this. _But_ Rust also includes some really nice syntax for passing errors through so I can write this: use failure::Error; fn foo() -> Result<String, Error> { may_return_an_error()?; might_return_a_different_error()?; if some_condition { return Err(MyPackageError { detail: "..." })?; } Ok("foo") } The `failure::Error` type automagically wraps any error. That means I can go through many levels of my stack returning `failure::Error` and at any point in the call chain I can decide to examine the error type instead of writing `some_func()?;`, which would pass the error back. The only part I don't like is that I need to add a `?` when I return my own errors. I don't totally understand this but from what I sort of understand the reason is that `?` invokes `.into()` on the returned object, which is what lets me return a (wrapping) `failure::Error` instead of a `MyPackageError`. The upshot of all this is that "ignoring" errors is quite easy, as long as _somewhere_ in my call chain I actually do handle the error. The type system will enforce this for me, as attempting to treat a `Result<String, Error>` as just a String will cause a compile time error. I have to unwrap it and either ignore the error (which would lead to a runtime panic if there _was_ an error) or do the right thing, which is to explicitly handle both the ok and error cases. I'm working on a CLI program using this system, and I can bubble all the errors up to the main entry point of the CLI. At that point I can turn errors into a print to stderr and an appropriate exit code.
- oppositelock 7y agoI've been using Go heavily over the last five years, starting with 1.2. Whatever warts the language has in its design, the error handling being one, it's still an excellent language for getting work done quickly, and correctly. I'm far more productive in it than Java, C++, or Python, and I'm old enough to have done lots of Pascal, Lisp, and more scripting dialects than I can count. Having been writing software over the 30 years that you mention, it's clear to me that Go has not been isolated from language design, it's just made a choice to stay simple. Java, C++ and Python have exceptions to keep you from littering your code with error handling, and code with exceptions is hard to read. The line of code you are looking at any point in time may instantly jump to another place, effectively, having "goto"'s evil cousin, the "comefrom". Somewhere, in your code, is a "comefrom" statement which runs immediately after the statement you are looking at, you're not aware of its existence. My Java/Python for production code ends up looking a lot like Go in terms of error handling, because you have the most context about an error at the point where it happens, so that is the best place to deal with it. I believe that Go 1.x will have the staying power of C.
- carc 7y agoAren't you supposed to check all the possible errors in golang anyway? How's that any different than java code being littered with error handling code?
- apta 7y agoExcept it's possible to miss checking errors in golang (accidentally either by not assigning the return value, or by overwriting a previously assigned error). Whereas in a language with exceptions, this is not possible unless by explicitly adding code to ignore exceptions. Do you ever see golang code that handles errors returned from fmt.Println?
- carc 7y agoI think I agree with you - I'm just trying to understand the other perspective from someone that seems to think not having exceptions is an improvement. I've mostly worked using java and go and to be honest I think I prefer exceptions because you should (almost) always check error codes in go anyway, and if you forget, you just made debugging much more difficult.