[HN Gopher] Experience Report: 6 months of Go ___________________________________________________________________ Experience Report: 6 months of Go Author : eatonphil Score : 187 points Date : 2022-04-30 16:07 UTC (6 hours ago) (HTM) web link (typesanitizer.com) (TXT) w3m dump (typesanitizer.com) | TheBlight wrote: | I find that people with much more C/C++ experience (>5 years) | tend to appreciate Go the most. Present company included. | tptacek wrote: | C, yes. C++, definitely not. | pjmlp wrote: | Not really, for Rob Pike's desilusion. | | https://commandcenter.blogspot.com/2012/06/less-is-exponenti... | psyclobe wrote: | I think you meant to say 'C Experience'. | | No C++ coder I know (at least the true C++ coders, e.g. ones | who have abandoned the 'Simplicity of C' mantra), find nothing | of use in go besides a draconian single truth model of 'the | best way to do things' similar to Python. | | That is really opposite of the C++ experience, where you have | millions of different patterns, techniques, and pitfalls at | every corner. Those who enjoy this, are definitely not go | enthusiasts. | | Now I'm not saying ones better than the other, but I really | doubt C++ core find Go attractive. Go was invented on the | premise that 'We hate C++' or something to that regard. | dilyevsky wrote: | > That is really opposite of the C++ experience, where you | have millions of different patterns, techniques, and pitfalls | at every corner. | | Exactly right. And everyone who is serious about the mission | and not "enjoying the process" is so so tired of c++ with its | "millions of different patterns, techniques, and pitfalls at | every corner" and c++ devs that need to be convinced not to | do crazy shit just bc they can | TheBlight wrote: | When I finished coding C++ professionally the general | sentiment of the team was to use less C++ than more. When | possible, it was advised to treat it more like C but with | classes. The goal was stability above all else and easiness | to reason about (for everyone.) That's why when Go was | announced its design direction made perfect intuitive sense | to me and anyone that spent years writing mission critical | C++ on large teams. | dilyevsky wrote: | Yes but many organizations make agreeing on "using less | c++" very hard these days. Google kind of managed with | their strict review/readability process and pretty much | mandatory tooling but it's damn near impossible in many | startups to do the same without a huge political capital | to spend. Go takes that challenge from organization level | and largely pushes it down to language level. | hamburglar wrote: | Well, good to know that after my decades of C++ experience, | I'm still not a "true C++ coder." You certainly can't lose | with an argument like that ;) | pphysch wrote: | C++ ICs won't find much to love about opinionated Go, but | once they start leading/managing a team, they will like it a | lot more. | LukeShu wrote: | I like to describe Go as "what you wish C were while you're | writing C". Not "what C should be", that's probably Rust or | something. Go is C, with the pain points filed off. | omginternets wrote: | I've noticed that people coming from Python tend to appreciate | it as well. The fast compile-times make the language _feel_ | dynamic, the concurrency story is much nicer than in Python, | the type system is helpful without being pedantic, and | interfaces are safer duck-types. | stjohnswarts wrote: | Type checking and lack of dynamic everything is a relief :) | kgeist wrote: | >I find that people with much more C/C++ experience (>5 years) | tend to appreciate Go the most. Present company included. | | In our shop there's plenty of former C++ devs who are now | writing in Go and enjoying it, myself included. Initially | everyone was skeptical and dismissive of the language to say | the least ("no generics? ha!") but as soon as we started | developing and releasing our first microservices people began | to appreciate how smooth and painless the whole development | process had become. I remember with horror our C++ projects' 2 | hour long rebuilds, 1 GB debug binaries, days of chasing memory | corruption heisenbugs... It's symbolic that the last C++ talk I | attended before fully transitioning to Go was by a guy from | Intel who spent 2 hours talking about how they abused templates | to write allocators which allocate other allocators at compile | time... Fortunately Go devs don't have this cult of | overengineering that's plaguing C++, we're getting things done | and release cycles are now much shorter and with less stress. | honkycat wrote: | My golang wishlist: | | 1. A error return syntax like =? In rust. | | 2. Sum types | | 3. Pattern matching | | There is a lot of boilerplate but I've found libraries like | Uber/fx help a TON. | | Also I've been doing a lot of codegen using a library that can | parse a package and return an ast API whose name escapes me right | now... | | Overall I think the language is in a great place with generics | added | devmunchies wrote: | sum types are my number one. the frustrating part is that it | _almost_ has it with type sets in interfaces, which you can use | in function signatures, but you can 't use them in definitions | of other types. | | example, given an interface that is scoped to 4 types: | type PlayingCardSuit interface { Diamond | Spade | | Heart | Club } | | I can use that in a function signature: func | PrintCardSuit(c PlayingCardSuit) { ... } | | But i can't use it to compose other types: | type PlayingCard struct { Suit PlayingCardSuit // | error Rank string } | matthewfcarlson wrote: | Man 10 seconds for a build and it's already running? It's like a | 10-14 minute build on a high end i9 for me and then 2-4 minutes | to flash the new binary on the hardware. | | It is hard for me to believe that a 30 second to 10 second build | time speed up would be noticeable in a day to day workflow. But | maybe I'm just envious. | t43562 wrote: | Yes, very negative. I can't stand how miserable it can be to fork | a library and in general to ensure I'm importing the correct | version of the thing I need to import when it's not in github. | | There are other compensations - faster than python but still has | decent regexps. Good crypto libraries. Channels (make life easy) | and massively scalable goroutines. ...trivial cross compilation | ... and many other things. | | I still find python much easier but, for example, I have very | little reason to bother with C++ or Java now. | benreesman wrote: | AtNightWeCode wrote: | At this level I was more annoyed about what goes into the libs | and not. Very flawed. Scoping was mentioned with defer but it can | be confusing in other cases too. | | I think if the progression of GO had been the same as we have | seen with Javascript, C# or even Java. Then GO would have been a | relevant general-purpose language today instead of this safe C | alternative in niche areas. | tgv wrote: | Bit negative. I agree on some points (e.g. error checks on | enums); other points, you simply have to learn to adapt to | (initialization). The rest are (very) cosmetic preferences | (overloading brackets; the compiler doesn't inform about typos). | | The biggest negative for me the author overlooked is the lack of | non-nullable types. The positive thing the author doesn't mention | is that it's easy to write in, nor that threading is really easy. | typesanitizer wrote: | > The biggest negative for me the author overlooked is the lack | of non-nullable types. | | I did mention the pervasiveness of nil and the lack of sum | types as negatives. | | > The positive thing the author doesn't mention is that it's | easy to write in | | I didn't mention this because I think the "ease of writing" is | superficial; thinking through edge cases is something that | frequently consumes a lot more time than literally typing the | code out. | | Also, given that the compiler doesn't really give any useful | suggestions when your code is wrong (something that happens | more often when you're less experienced in a language, like I | was here), ease of writing takes a significant hit. | | > nor that threading is really easy. | | I didn't cover this because I haven't had the chance to work on | much code using goroutines, and I've tried to ground the post | in what I have actual experience with. | barsonme wrote: | Like every language, there are plenty of 'problems' with Go. But | many of these seem like off-the-cuff complaints instead of | thought-out criticisms. To choose an arbitrary example: | nil is sometimes equivalent to an empty collection but sometimes | causes a runtime crash. var a []int // initialized to | nil _ = append(a, 1) // OK var m map[int]int // | initialized to nil m[0] = 0 // panic: nil dereference | It's not super clear to me whether there is some systematic rule | governing when nil causes a crash when using a built-in | operation. Are crashes specific to write contexts and non-crashes | specific to read-only contexts? I don't know. | | The author is comparing two different operations. The fact that | they're operating on nil variables is irrelevant. | | The equivalent slice operation is not the builtin function | "append", but rather assignment: a[0] = 1. This will panic in the | same manner as m[0] = 0 (albeit with a different panic message) | | A map-based operation that's equivalent to "append" would be a | (hypothetical) builtin function like "func assign(m map[K]V, key | K, value V) map[K]V". | | The language could define define that map assignments auto- | vivicate the map when necessary. But that would cause each map to | incur an additional allocation, even if it's never assigned to. | And then for consistency you'd need to apply the same thing to | slices. | nemothekid wrote: | > _A map-based operation that 's equivalent to "append" would | be a (hypothetical) builtin function like "func assign(m | map[K]V, key K, value V) map[K]V"._ | | But why isn't there an `assign` function? I've been writing Go | for a long time, and from a design point of view, I _really_ | don 't like append. It's magic, and as the OP has shown, it's | trips up new programmers. If Go was consistent, there shouldn't | be an append; but instead we got "generics for this one | function". The problem isn't really `nil` that acts confusing | (in this case), it's `append` acting like a function when it's | really a magic compiler directive. | | I personally think this criticism is a problem; zero-values, | especially nil ones, can be a major footgun. I've had a couple | experiences where a production service crashed because someone | accidentally tried to use a nil interface even though they had | already nil-checked it. | morelisp wrote: | append is the opposite of magic. it's exposing the details of | a realloc when 99% of the time you want to keep the same | binding. That's probably also why map doesn't have an | equivalent; in the case of hash tables it's more like | 99.999%. | deschutes wrote: | Why require types have a meaningful zero value? | | It's not necessary for value semantics. You can require that | values be initialized and the machinery for that doesn't seem | that bad. | | Zero values seem as arbitrary as requiring all types be | equatable. | [deleted] | barsonme wrote: | I'm really not sure what your comment means, sorry. | deschutes wrote: | So, my understanding is that: 1. Unless otherwise | initialized, types have a zero value in go. For pointer | types that zero value is nil. 2. It's encouraged for this | to be meaningful. That's opposed to say java, python, or | C++ where invoking a method on a null object is a runtime | error (or undefined behavior). So for example, appending to | a 'nil' slice is the rough equivalent of trying to add to a | null List in java. I don't see how the go approach makes | anything simpler. | | Value semantics are simply that the value of an object is | all that matters. For example, two int objects '5' are the | same from a value-semantic perspective. I guess this | implies all objects have some value. This requirement can | be satisfied by requiring they be explicitly initialized. | | Equatability is a requirement of objects in java. Not all | types of objects are equatable beyond their identity (some | unique identifier) though. For example, how do you equate | two functions? To me this parallels the decision in go to | make zero values a thing beyond a runtime error. | barsonme wrote: | > I don't see how the go approach makes anything simpler. | | The "append" builtin doesn't "invoke" anything on the nil | pointer. It's more or less (in Java-ish pseudocode): | static Slice<T> append(Slice<T> s, items ...T) { | if (s == null) { Slice<T> newSlice = new | Slice<T>(items.size()) | newSlice.pushBack(items) return newSlice | } if s.hasEnoughCapcityFor(items.size()) { | s.pushBack(items) return s } | Slice<T> newSlice = new Slice<T>(s.size() + items.size()) | newSlice.pushBack(s) newSlice.pushBack(items) | return newSlice } | | which is a perfectly reasonable utility function in | pretty much any language. See, e.g., realloc(3). | | > Value semantics are simply that the value of an object | is all that matters. For example, two int objects '5' are | the same from a value-semantic perspective. I guess this | implies all objects have some value. > > Equatability is | a requirement of objects in java. Not all types of | objects are equatable beyond their identity (some unique | identifier) though. For example, how do you equate two | functions? To me this parallels the decision in go to | make zero values a thing beyond a runtime error. | | In Go, having a "meaningful" zero value for a type means | that you don't need to initialize the type before using | it. For example: var b bytes.Buffer | b.WriteString("hello, world") | | instead of b := bytes.NewBuffer(nil) | b.WriteString("hello, world") | | Or, as another example: type | BinarySearchTree struct { ... } func (b | *BinarySearchTree) Contains(key string) bool { | if b == nil { return false } | [...] } | | Go makes this possible by not requiring constructors like | other languages do (e.g., Java). | spinny wrote: | You can evoke a method on a nil object in Go because it | handles methods as functions that have an extra first | parameter. take a look here: | https://go.dev/play/p/2SRU26mvfnL | barsonme wrote: | Some complaints also seem like a lack of experience using the | language. For example Initialization with make | behaves differently for maps and slices: m := | make(map[int]int, 10) // capacity = 10, length = 0 a := | make([]int, 10) // capacity = 10, length = 10 (zero | initialization) b := make([]int, 0, 10) // capacity = | 10, length = 0 So not only is there an inconsistency, | the more common operation has a longer spelling. | | In my experience, the two-value (explicit capacity) form of | "make" is significantly _less_ common than the single-value | form. Indeed, gripping through the stdlib shows "make([]T, n) | is much more common than "make([]T, n, m)". | | I agree that appending to "make([]T, n)" is not an uncommon | mistake. But, in general, you can avoid that problem by | assigning to specific indices instead of using append. | | I think the place I regularly use "make([]T, 0, n)" is when I'm | collecting items from a s := make([]K, 0, | len(m)) for k, v := range m { s = append(s, k | } | typesanitizer wrote: | > Some complaints also seem like a lack of experience using | the language. | | > In my experience, the two-value (explicit capacity) form of | "make" is significantly _less_ common than the single-value | form. Indeed, gripping through the stdlib shows "make([]T, n) | is much more common than "make([]T, n, m)". | | I've written a fair bit of C++, where this pattern is very | common. IME in 95%+ of the cases, what one wants is a vector | with a capacity without initializing it, because it will be | filled up right away. | | I'd argue that make([]T, n) is more common in actual Go code | precisely because it has the shorter spelling, not because it | has the exact desired semantics. | barsonme wrote: | Ah. But this is writing C++ in Go instead of writing Go in | Go. | | In C++ I'd write something like | std::vector<T> vector; vector.reserve(10); for | (auto i = 0; i < 10; i++) { vector.push_back(...); | } | | but in Go I'd write vec := make([]T, 10) | for i := 0; i < 10; i++ { vec[i] = ... } | | Just like in Rust I might use iter().collect() or whatever. | aledalgrande wrote: | My experience with Go is that it is pretty low level compared to | languages like Ruby, but higher level than something like C++. | | I'd be curious to hear what people build with it at work, is it | mostly microservices or are there also monoliths around? Is it | more APIs or background workers? | | What do you think it's an interesting project I could build in my | time that shows me where Go shines? | ufmace wrote: | I share at least some of the author's criticisms of Go. But it | neglects to mention a few of my favorite things - the massive and | mostly capable stdlib and the easy cross-compiles. How many | compiled languages can you be on any of Mac, Windows, or Linux, | and compile a binary for all 3 instantly with just a env var that | always works no matter how complex your code or how many | libraries you're pulling in? I can't think of any offhand. | | I like Rust better as a language for a number of reasons, but it | unfortunately falls down on both of those. There's also a | noticeable difference in how they're used based on the number and | maturity of packages available for various things. Most of the | Rust world seems to be more focused on low-level stuff. You can | do stuff like web services and query web APIs and talk to | databases, but there's likely to be only 1 or 2 crates for it, | and they're probably maintained by a single person who (like most | normal people) sometimes just stops doing anything for months or | years. Golang is really made for the web services world, and it | shows in the packages available - it seems a lot more likely | there will be many top-quality and well-maintained packages for | doing any web service like thing you might want to do. | lelandbatey wrote: | > Cannot make a type in a foreign package implement an interface | | I would only push back on this point, and this point alone. Sure, | it would be very convenient for me, an author to be able to just | extend the implementation of anything that I import. However, | when I am not an author and am instead a _reader_ of code, doing | this is the number-one way to causally make code totally | unreadable. By allowing anyone to extend a type anywhere, it | makes it impossible to _just_ read the code. If the package that | declares a type is the sole controller of that type, then reading | code is easier. | | _" Where is the definition of this method?"_ Answer: It's next | to everything else for that type, in the package where that type | is defined. However, if anyone can extend any other type, | figuring out what methods even exist for a type becomes so | difficult that you are forced to use a language-server (with all | related dependencies installed) in order to use "go-to- | definition". If someone wants to understand the code, requiring | them to have a full-fledged IDE and/or development environment is | pretty awful and quite onerous, and makes codebases tough to get | into. | | For example, take this line[0] from a tutorial[1] on creating a | simple snake game using the Bevvy engine in Rust. The main() | function wires up loads of entities, including a `snake_eating()` | function. But it also calls a mysterious `.after()` method of the | snake_eating() function. If you do a ctrl-f on the file, you | won't find an .after() method defined for the function | snake_eating(). Just reading the code in that file, the only file | in the program, you'll never find what the `.after()` method does | or where it comes from. Only by opening that code in an IDE with | go-to-definition will you learn that it's set via an automatic | macro in the bevvy library[2] which causes all functions of | certain signatures to implement the System trait. Which... is | pretty tough to get into, and means just reading code without | computer aid is effectively impossible. | | In your languages, please don't allow packages to extend the | interfaces/traits/types of other packages. | | [0] - | https://github.com/marcusbuffett/bevy_snake/blob/0.7/src/mai... | | [1] - https://mbuffett.com/posts/bevy-snake-tutorial/ | | [2] - https://github.com/bevyengine/bevy/discussions/1137 | typesanitizer wrote: | I sympathize with your point that being able to use simpler | tools (such as grep) is often a good thing instead of having to | rely on heavy-duty functionality (such as a full IDE/language | server). That said: | | - Ctrl+F in a file specifically isn't guaranteed to always | return a hit with Go since a package can span multiple files, | and you can have methods in other files. | | - Increasingly many tools (like Sourcegraph and GitHub) make | rich code navigation available on the web, without having to | use an IDE or check out the code locally. Yes, it's not perfect | for all languages, but it's improving every day. Similarly for | documentation tools in different ecosystems -- I think both | Haddock (Haskell) and rustdoc support cross-linking references | to definitions. | | - If you have textual code search for dependencies, you can | still grep through the code with a regex like `func \\(.* | *?MyType\\) myMethod`, and it will give you hits in other | packages too. | lelandbatey wrote: | I also agree that if I had the full IDE like power | everywhere, then that'd be great. And having those tools in | more places is also great. | | Ctrl-f being limited to a single file is a downside, but it | degenerates to cases where you may have to ctrl-f in 5 other | files in the folder up to maybe 50 files in the folder for a | huge package. And from there, it's narrow-able based on | things like names of files. | | However, allowing any package to modify any other package | causes that number to explode well past what is ever humanly | tractable. It goes from "inconvenient" to "impossible". | | Language designers, please design your language so we can | read it with our mere human eyes. | ngngngng wrote: | > doing this is the number-one way to causally make code | totally unreadable. | | Absolutely, my immediate thought as well. I believe my | experience reading and writing Go has been more pleasant than | in other languages because people writing Go seem to shy away | from hidden behavior, and I think rigidity like this helps. | | Also, to the author, "anyways" is not a word. | Xeoncross wrote: | I really wish nil didn't exist in Go. It causes those familiar | runtime panics like "cannot __ on nil" we love from those loose | scripting languages like node. | karmakaze wrote: | Zero values are one of the tenets of Go. It's like saying I | which Go had exceptions which is to say you'd prefer not-Go. | Ironically Go was promoted as the anti-Java which we know is | littered with NPEs. | Xeoncross wrote: | I love zero values, I just wish Go was better at realizing I | was using one. | kgeist wrote: | Nil values could be kind of tolerable if the language was | always explicit about what is nillable and what is not. If you | see something like "MyStruct" you can never be sure if it's an | interface (can be nil) or a value (cannot be nil). | tptacek wrote: | I understand the ambiguity you're talking about here, but how | often does this actually bite you in practice? There are | other context cues that you're dealing with a struct and not | an interface, and the nil-ness of a return value isn't how | you idiomatically communicate success or failure, even for | things like map lookups where it would be somewhat natural in | a C API to do that. | synergy20 wrote: | Go is my go-to language for cross-platform network application, | the builtin https is enough alone, plus I can ship a true single | binary in the field, can not be easier to upgrade. For other use | cases, I use c/c++/python/typescript. | ThinkBeat wrote: | This is meta but in most fields 6 months of experience is not | considered significant. | | "Things move so fast" | | Not really. We are doing mostly the same as always on the front | end. Put dots on screen at the rigt time,right shape, colors etc. | | With over 50 years going at it, you would think this was a | trivial effort that was well understood and solved decades ago. < | | Esp, after hardware converged to the rather uninspiring selection | left. | | (This might actually get better now with manny companies getting | in on desiging CPUs GPUs and so on I have some hope for a return | to competition on those fronts ) | blaisio wrote: | Go is my default language for any non-client-side project. That | said, I agree with all the criticisms, except for the one about | unused variables/imports and the lack of warnings in the | compiler. I'd also add one of my own - I'm an expert go | programmer, but I still panic sometimes when dealing with error | handling. It is still not as easy as it could be to produce | useful error return values. | luxurytent wrote: | Indeed. panic is not overly recommended but there's a few | places you can nudge it in that just make sense, rather than | ensuring the error will successfully propagate up the chain. | blaisio wrote: | Haha I meant panic as in become scared, not the go panic. | luxurytent wrote: | Hah, it works for both I guess :) | coolgoose wrote: | They forgot to add that Go's date handling is a bit... different | also :) | typesanitizer wrote: | I didn't mention it because I didn't run into date handling | code myself over the past 6 months. I've focused on the | positives and negatives that I've run into in practice. | truffdog wrote: | Honestly I kind of prefer it to the usual Unix date clone | format. | latenightcoding wrote: | Go is an ok laguage with a top-notch runtime | omginternets wrote: | Ha. This is quite true. I wish there were an (obvious) way to | build languages on top of Go's runtime, in much the same way as | Clojure/Scala/etc run on the JVM. | Karrot_Kream wrote: | I think having the Go runtime be specific to Go is what makes | it such an effective runtime. | omginternets wrote: | Why? | | And more to the point: binding an alternate syntax to the | runtime wouldn't make it any less specific to Go. | luxurytent wrote: | This is a good article. I am curious how the author compiled it. | Everytime they came across a quirk they'd log it in a journal? | Fun :) | | Figured I'd comment on at least one item which I ran into | recently. | | > Sends and receives to a nil channel block forever. | | This did feel bizarre to me too, until I realized how well this | works with select blocks. That IMO, is the primary justification | for that design choice. | tedunangst wrote: | > During this time, I've been making notes of speed bumps I've | run into, as well as things I've liked about Go. | | This is a smart approach to a new language. Especially since | you now have a list of things to recheck your early code for. | cannabis_sam wrote: | Oh my God, as an average moron who decided to pin his | professional growth in my current company on learning Go and | Rust, these threads are paying dividends that I could not have | imagined in my wildest dreams! | o_1 wrote: | Go is a tool. I haven't seen a language get as much directed | criticism on HN in a long time. I appreciate genuine feedback, | but only when its useful and direct. I can't remember a time when | Java got ripped on HN this hard. Distancing oneself away from the | religion of languages is the best thing I've done thus far. I | "hope" the merits of language justify direction of software | rather than the hype. | Thaxll wrote: | It's pretty simple, people don't understand why their favorite | language is not as popular as Go eventhough they think it's | vastly superior. | sremani wrote: | Did you even read the article. Also, it was written by an | individual and their experience with the language. The article | merits a read or at least a skim. | | I cannot speak for others, but I do not see a cabal of people | conspiring to bring down Golang on HN. Yes, esoteric languages | get love and practical languages like C++/Go/Java et al. get | hate because people are more familiar with them and see their | warts. | | The more a language is criticized the more often it is used. | daviddever23box wrote: | It appears that many of the issues I see in articles of this | type are often RTFM-level issues, especially when calling out | linter exceptions (which a quick scan of the source code of | an individual linter would help to explain). | abirch wrote: | Do we have an example of a language getting better with | critism? I love the evolution of JavaScript but not sure if | blogs shaped it. | llanowarelves wrote: | Some people still call it a toy language because they are | mentally like 15 years behind and think it's unreadable | nested Jquery event emitter callbacks and Object.prototype | overloading. The rest of us pushed for / picked up on the | rapid improvements in tooling, spec, and best practices, | causing "JavaScript fatigue", though that has slowed. | bluejekyll wrote: | Java is like 15 years older than Go. Go had the opportunity to | learn from Java. And in some ways it's better, fast compile | times, simple to deploy binaries, etc. | | But the language itself was a step backward from Java in some | ways, and only recently has gained some of those features. | | I don't think it's wrong to say that Go in its desire to be | simple left a lot to be desired for many people. | sremani wrote: | There is a better Java, its Kotlin and its embrace has been | lukewarm by community. I think golang serves an interesting | purpose and it was always meant to be a better C not better | Java. | dagmx wrote: | Depends on which community. It was extremely welcomed by | the Android community. | pjmlp wrote: | Actually it was been heavily pushed by Android team, | which knowingly stagnate Android Java, with the agenda to | replace its use outside the Android system libraries. | | Even Android 13 update to a newer Java 11 LTS subset | seems to be caused by not losing the ability to use | specific Java libraries than anything else. | bluejekyll wrote: | I've always seen Go as far more like Java than C. They | facilitate the use cases. Go isn't a good C replacement for | mostly the same reasons Java is not. | | People are adopting Kotlin, I've seen many coworkers using | it for new projects. It becomes a bit more complex if | you're trying to integrate it into an existing large code | base, of which there are many in Java. | tptacek wrote: | A weird special-pleading argument, as Java is intensively | used in our industry, including for new projects, and | language features are engineering decisions, not moral | lessons. | | Of course, people have to come up with special pleading | arguments about Go, because most of the complaints (and all | the most pointed complaints) are shared by the other most | popular languages in industry. You can't get any traction | with a takedown of Python, only with Go or Rust. So these | kinds of complaints are invariably pretzled up into some form | of "Go is a terrible language _for the good language it is | compared to most other languages_ ; it may be better in many | ways than its predecessors, but it had no business not being | even _more_ better ". Well, peachy. | | Don't have a programming language as part of your identity. | It takes you weird places. | hamburglar wrote: | I'm not sure what's more predictable: the go-hater-is-my- | identity posters or the defensive go-coder-is-my-identity | posters responding to the OP line by line. (Or perhaps the | smug above-it-all posters such as myself). | | As a likely certifiable go fanboy, I like the article and | see a lot of stuff I agree with. Still enjoy programming in | Go. Will error handling ever be less verbose and | repetitive? Not sure. If not, it's not a dealbreaker for | me. And I do acknowledge that the fact that all the error | handling is right there in my face makes it easy to reason | about. Pros and cons, folks. | tptacek wrote: | I thought this article was a cut above a lot of the Go | criticism I've read. Language critiques are good! Where | we tend to get in trouble is language _ordering_. | stjohnswarts wrote: | I agree, it was well supported rather than just one big | bitch-fest. | pjmlp wrote: | The irony is that from point of view from Go folks, Java's | type system is already PhD skill level. | tptacek wrote: | I don't know who language like this is supposed to | convince. | pjmlp wrote: | Convince who? Go folks? | | That is a lost battle, like fighting windmills. | tptacek wrote: | If you're not here for discussion, why are you here? | pjmlp wrote: | Maybe you can spend the rest of the evening asking the | same to everyone else that shares the same opinion as | myself on this thread. | tptacek wrote: | I don't know who language like this is supposed to | convince. Apparently: people who already agree with you? | pjmlp wrote: | I am not going to convince anyone, I am not a missionary | doing conversions, specially from a group that workships | statements like, | | "The key point here is that our programmers are Googlers, | they're not researchers. They're typically fairly young, | fresh out of school. Probably learned Java, maybe learned | C or C++, probably learned Python. They're not capable of | understanding a brilliant language. But we want to be | able to use them to build good software. And so the | language we give them needs to be easy for them to | understand and easy to adopt." | tptacek wrote: | You're definitely not going to convince anyone if the | only thing you can inject into a discussion is contempt. | The "Smug Lisp Weenies" tried that course; we're not all | using Lisp. In fact: their heyday was the great | flourishing of the least-Lisp-like languages. | farmerstan wrote: | Java has gotten a ton of hate since it's inception. Now it's so | embedded that no one really cares anymore. | thisarticle wrote: | I feel like this is a much more fair and well written blog post | than the other Go blog post from a few days ago. | PrayagS wrote: | You mean this one | https://news.ycombinator.com/item?id=31205072? | thisarticle wrote: | Yes. | msie wrote: | I would be curious to know what he thought of Swift (having | worked on the compiler). The language seems to be the opposite of | Go in including every language feature under the sun. | typesanitizer wrote: | > I would be curious to know what he thought of Swift (having | worked on the compiler). The language seems to be the opposite | of Go in including every language feature under the sun. | | Are you asking from a language design perspective? Or from an | implementation perspective? | | From a language design perspective, yes, Swift has a lot of | features. Most of these features exist for good reasons. | | - First-class Objective-C interop: Needed for initial adoption | and migration, since Apple's SDKs were all Objective-C. (See | also: Kotlin and Java etc.) | | - Protocols with associated types: Writing generic code with | constraints makes surfacing type errors easier compared to | templates. Associated types enable many natural patterns of | programming. | | - Library evolution: Being able to evolve APIs without breaking | ABI is super important for a platform. | | - Support for DSLs: Swift is used heavily for UI programming, | and there is a convergence across languages in terms of having | DSLs for making building UIs easier. | | - Use of weak pointers (vs having a tracing GC for cycles): | Better for Objective-C compatibility. | | - Async/await + actors: Trying to balance usage of Dispatch | (which is the platform API) with newer programming patterns, | while still being able to compile in a way with low resource | usage. | | - Upcoming C++ interop: Many big iOS applications use large | amounts of C++, so better interop would make Swift usage easier | for them. | | Does that mean I think every feature of Swift is perfect? No. | For one thing, I think method overloading is way too flexible, | which is what causes exponential time for type inference in a | bunch of cases. | BonoboIO wrote: | Bit of offtopic: | | Best bash of a programming language | | ,,The Perl Jam: Exploiting a 20 Year-old Vulnerability" | | https://youtu.be/noQcWra6sbU | | And part 2 ,,The Perl Jam 2 The Camel Strikes Back" | | https://youtube.com/watch?v=RPvORV2Amic | nu11ptr wrote: | I hesitate to say I thought this was a good article because it | was highly negative and I don't think it focused enough on the | positives (of which I think there is some - for example, it has | very nice and highly performant lightweight threads with full | blocking semantics via goroutines). That said, I'm not really a | fan of Go even though I wrote it for a few years and I think the | article covered pretty well why I don't generally care to write | it (even though I don't agree with everything they said). | | I think I would say overall that I like the idea of Go better | than I like Go itself. I like the idea of a simple language, but | not the choices they made. I like the idea of simple tooling, but | don't find their tooling simple to use (ironically, Rust's | tooling is much simpler to use IMO). By being less expressive, it | actually makes it more confusing to use IMO (in the same way I | find dynamic languages more confusing because without static | types, I'm less sure about what args a function takes). By | keeping null, not having sum types, etc., I don't trust the code | I write. In the same way I don't trust any code I write in Python | unless covered by a test, I don't get much confidence in Go's | static typing due to their implementation choices. | | In summary, I don't think 'simple' is the right metric because it | doesn't necessarily make writing code 'easy' or safe. For me, | languages that are highly 'logical' and 'compose well' are much | easier to use in practice. A language should try and find the | simplest way to express X, not remove X as a concept IMO, or else | code simply will not scale or compose once past a few thousand | lines. | karmakaze wrote: | > highly performant lightweight threads with full blocking | semantics via goroutines | | If you've ever tried to doing any actual high-performance work | using the Go mantra "share by communicating" with channels, | you'll quickly find that it isn't high performance. Any Go | program that actually needs high performance will break all the | rules for thee and use undocumented things like sync/atomic. | lrem wrote: | Heh. For the past 3-ish years I operate a flock of | microservices in Go. I don't really write the code, but do | quite a bit of reading to debug and fix stuff. This month | I've seen a channel in production code. I did a double take | and took my time to reflect on the language. | kgeist wrote: | Interestingly, even though Go is most popular for writing web | services and promotes channels, we almost never use channels | in our microservices written in Go because they are not | persistable. If you care about not losing your data on power | loss or a panic, your work queues have to be persistable and | retriable. Channels IIRC are just in-memory queues with | mutexes under the hood and provide nothing of that. We only | use them for a few tricks/hacks like listening to signals to | safely stop goroutines. | tptacek wrote: | That makes sense: channels are a way of organizing | concurrent programs, not a way of organizing distributed | systems. They're a tool you might use to build a persistent | work queue (or, like many programmers, including people | working on the Go stdlib, you might use other | synchronization constructions), not a work queue | themselves. | kgeist wrote: | When I first learned Go, my impression (and also | tutorials usually imply that) was that they are a nice | elegant way to build concurrent pipelines where goroutine | A sends work items to goroutine B which can, in its turn, | send some work items to goroutine C etc. But in practice | for robustness we prefer persistent queues for such | pipelines because on power loss, a panic, or if your | application is simply being killed on redeploy, your | program can lose data or end up in an inconsistent state, | because whatever was the in the channels is completely | lost (and the work is already half done). So it leaves us | with only a few use cases where they're really useful | such as basic goroutine coordination. I think what you | are saying is that they're a synchronization primitive | akin to mutexes, atomics etc., no more no less, and I'm | fine with that, but then it's not clear why then channels | have a special syntax and why they are sold as one of | Go's strong points, if it's just a niche synchronization | primitive. The only useful case I found for them was to | send an empty struct to a goroutine to signalize that | there's a new work item in the persistent queue, to avoid | having to poll the external queue too often. I wonder if | other web devs have experience similar to ours, or maybe | there are other use cases for channels we are not aware | of. | tptacek wrote: | The point of channels is to have communication between | threads that's easier to reason about than explicit | locks. Rather than, for instance, unlocking a map to | update it, you treat a single thread as a "server" for | that map. If you don't want to structure your programs | that way, you'd just use mutexes. | withinboredom wrote: | In older web languages, like PHP, I would reckon it's | like making a self-http call where you don't care about | the result and just want to trigger some work elsewhere | in the application async. I guess with Go channels, you'd | lose out on concurrency, automatic retries (depending on | infra), and debuggability -- you could do the same with | Go and http calls though and just drop the channels | completely. | | But yeah, sometimes things break. We rely on things like | nginx options to retry GET requests and idempotency in | the design; failing gracefully (via a shutdown callback | to always return something to a caller); ensuring work is | completed before writing a successful response anywhere, | along with being idempotent; and, ensuring there's | observability in every long-running task. | Mo3 wrote: | > That makes sense: channels are a way of organizing | concurrent programs, not a way of organizing distributed | systems. | | Exactly. | throwaway894345 wrote: | I don't think channels were ever billed as peak performance | abstractions. I certainly don't use them as often as I use | mutexes or atomically for parallel code, but frankly I rarely | write parallel code in the first place because it's harder to | maintain and I rarely need the performance (single threaded | Go is really performant already compared to other languages | in its class). | Mo3 wrote: | I have _not once_ had to use sync /atomic and I wrote and | maintain a massive data aggregation and quantitative | algorithm platform 100% in Go at work. | | Channels in Go are not a function, they are a primitive type. | | In contrast to semaphores aka. mutexes, channels are highly | recommended since, when used correctly, they can serialize | concurrent access very efficiently. Take care of how to use | them - do not pass tiny amounts of work over channels, pass | around a chunk of work and work in batches. | | Normally the work you will do per item will greatly exceed | the 90-250 ns it takes to move the item through the channel, | so it's just not worth worrying about. | | Channels are slower than copy() for just shifting bytes, but | in a simple scenario are about as fast as a naive self-made | channel implementation using the sync package. | | The choice of channels vs. mutexes is one of design, not | implementation. BOTH use LOCK XCHG and pay the price. | | Also, channels are blocking data structures. APIs should be | designed synchronously, and the callers should orchestrate | concurrency if they choose. If you just want to synchronize | access to shared memory then use a mutex. These are not good | use-cases for channels to begin with. | | As they say: Share memory by communicating, rather than | communicating by sharing memory. | | An easy example of this: Use a channel with a buffer of 1 to | store the current number, fetch it from the channel when you | need it, change it at will, then put it back for others to | use. | icholy wrote: | What makes you think sync/atomic is undocumented? | https://pkg.go.dev/sync/atomic | klabb3 wrote: | I was gonna answer: | | "Channel ops are wait free if the buffer has extra space | though, so that should be fast" | | But, I looked it up and channels indeed use locks even in | case of buffered ops. Sigh. I guess MPMC with scheduler yield | isn't the easiest thing to write and maintain. | Karrot_Kream wrote: | Unless you're using something like an RCU queue, you're | still going to have to lock operations around moving the | queue pointers, or you could have producers writing over | each other or writing into a slot a consumer has already | read from. | masklinn wrote: | Nit: sync/atomic is not undocumented: | https://pkg.go.dev/sync/atomic@go1.18.1 | lmarcos wrote: | > I think I would say overall that I like the idea of Go better | than I like Go itself. I like the idea of a simple language, | but not the choices they made. I like the idea of simple | tooling, but don't find their tooling simple to use | | I think I just had a revelation moment. This is exactly how I | feel about Go. I keep trying to use it for side projects and I | think I'm lying to myself... It's not Go what I like, but the | idea of it. | slimsag wrote: | so.. Haskell and Scala? Those would be the top two that come to | mind for me when someone says "highly 'logical' and 'compose | well'", "having sum types", 'more expressive', etc. | | I've heard they're great languages (haven't used them much | myself), but I probably wouldn't choose them for say an early- | stage startup. Go's value is in being a mundane, repetitive, | boring language. It makes it easy for any random kid to dive | into an existing code base and become productive, that can be | super valuable. | | There's a theoretical gradient of programming languages where | one side is "has a single expression for exactly your problem, | a single unique expression you've never heard of exists to | solve every possible problem" and the other is "requires | thousands of expressions to express your problem, but only a | thousand are available" - or something like that. | | My point? There's no perfect language, yet people are always | trying to find one. It's OK if you don't like Go and prefer | another language, the real devil / tradeoff is in the fact that | conformance to a single language (or set of languages) is such | a strong social phenomena. I think that's why people end up so | angry with viewed-as-subpar languages like Go gaining so much | traction: it limits personal choice of which language to work | in. | | I'm positive linguists aren't happy with English becoming the | global language either, but you've gotta admit - having a | global language is valuable. | typesanitizer wrote: | > There's no perfect language, yet people are always trying | to find one. It's OK if you don't like Go and prefer another | language, the real devil / tradeoff is in the fact that | conformance to a single language (or set of languages) is | such a strong social phenomena. I think that's why people end | up so angry with viewed-as-subpar languages like Go gaining | so much traction | | I hope my post didn't come across this way. To be clear, I | think Rust and Swift (as two examples that I mention multiple | times) have a lot of problems (slow compilation being a very | big one), and are by no means perfect. I'm not angry at Go's | popularity as much as wanting improvements to the developer | experience. | Thaxll wrote: | Scala and Haskell are pretty much "dead" language, I worked | with Scala in the past, it was impossible to find poeple | willing to work with it so everything was re-written in Java. | Milner08 wrote: | Im not sure its fair to call Scala dead. Its still pretty | widely used, at least in companies in London. Hiring is a | bit more competitive but we still get some great Scala devs | interviewing for the more senior roles. | | Disclaimer - I work with Scala every day, so am definitely | highly biased. | halfmatthalfcat wrote: | Scala is definitely not dead. Sounds like a recruiting | failure than anything about the language itself. | erik_seaberg wrote: | I learned Scala a while ago on the job, and I'm grateful | for that opportunity. Nobody on your team volunteered? | icedchai wrote: | I worked at a Scala shop about 10 years ago. Everyone had | their own preferred "dialect", kind of like C++, resulting | in too much whining and complaining during code reviews. | IMHO, the language is too complex. Also, the compiler was | slow, and the IDE support was plain awful (Eclipse was | especially bad, IntelliJ was better.) Keep in mind this was | over a decade now, so I'm sure things have improved. | vips7L wrote: | There's no bigger productivity killer than scalac and | sbt. | mjr00 wrote: | This was my experience as well. I worked at a place that | used Scala primarily in the "better Java" style and | enjoyed the language a lot. I moved to a different | company and the lead programmer there was a functional | purist who insisted on putting scalaz/cats into | everything and using Scala as Haskell lite, despite it | really not being appropriate for the use case. It really | soured me on the language. | pjmlp wrote: | I bet plenty of universities will keep using them beyond | our lifetimes. | | From that point of view they are doing quite well. | blakebreeder wrote: | you can also take a Latin course in college. how "well" | is Latin doing? | pjmlp wrote: | Good, it is still the official language in Vatican | documents, has an updated dictionary, and some European | countries see it as a CV requirement by most HR | departments when hiring for top management positions. | throwaway894345 wrote: | That's pretty good for a dead language, but not so much | for a living one to the OP's point. | cinntaile wrote: | > some European countries see it as a CV requirement by | most HR departments when hiring for top management | positions. | | This sounds utterly bizarre, I have a very hard time | believing this. Which countries would that be? | MereInterest wrote: | I can't confirm the specific example, but this sounds | like a smoke cover for nepotism and/or classism. If | you're not allowed to recruit solely from your personal | friend group, requiring that applicants be able to speak | a dead language let's you select that same group of | people while giving a flimsy justification. | tptacek wrote: | So, just like strong idiosyncratic preferences for | particular programming languages. | pjmlp wrote: | In France for example, it used to be that all good family | kids studied Latin, so high league universities and was | seen as plus on the CV, at least for 20 years when I used | to live there. | karmakaze wrote: | An F# program can be about as mundane as a similar Go | program, only concise. | throwaway894345 wrote: | This is like the C++ argument that you should just use C++ | because it has almost every feature so you can just use the | ones you want and ignore the others. Of course, in practice | you still have to deal with colleagues and upstream | packages that use features you don't like. Same deal with | F#--you _could_ write F# in a Go-like style, but you'll be | swimming against the current. And the only advantage is a | little less verbosity, which really isn't anyone's | bottleneck--people over-index on minimizing localized | boilerplate and ignore the costs of gratuitous abstraction. | nu11ptr wrote: | > so.. Haskell and Scala? Those would be the top two that | come to mind for me when someone says "highly 'logical' and | 'compose well'", "having sum types", 'more expressive', etc. | | I wrote Scala for years - it might be a bit too complicated | but overall pretty decent, but I hear good things about Scala | 3. I think the ML's, of which it takes inspiration from, are | probably a better match. OCaml (EDIT: or F# as another | comment mentioned) for instance is a pretty nice balance. | | Haskell is very nice and I think qualifies except that it is | pretty hard core purely functional with no punches pulled | (unlike ML), so is foreign enough for most people not to be | deemed a candidate. | | I like Rust probably best atm as it is "imperative but with a | functional flair", but doesn't qualify as easy I don't think, | but is definitely highly logical and composes very well (even | if obviously not finished yet). | | > I've heard they're great languages (haven't used them much | myself), but I probably wouldn't choose them for say an | early-stage startup. Go's value is in being a mundane, | repetitive, boring language. It makes it easy for any random | kid to dive into an existing code base and become productive, | that can be super valuable. | | I would probably agree on the novice programmer and picking | up Go quickly which is arguably the prime feature of Go. I | just question the quality of that code, and honestly, don't | feel Go is nearly as easy as touted. It is simple for sure, | but not always easy. I always had to look up simple things | like those magic comment compiler directives and is that | interface{} param a pointer or a pointer to a pointer? I just | remember the lack of expressiveness actually causing real | world confusion (for me at least). | | > There's a gradient of programming languages where one side | is "has a single expression for exactly your problem, a | single unique expression you've never heard of exists to | solve every possible problem" and the other is "requires | thousands of expressions to express your problem, but only a | thousand are available" - or something like that. | | While true, I would argue we have found a small amount of | constructs that fit well 80% of the time, and is | demonstrability better than half the constructs that fit well | 40% of the time. Trying to solve every problem with a new | construct is not worth it, but nor is the opposite extreme | IMO. | | > My point? There's no perfect language, yet people are | always trying to find one. It's OK if you don't like Go and | prefer another language. A real devil / tradeoff is the | prohibition of using languages that do not conform with past | (company) choices. | | Honestly not sure we've found the ideal language yet, and | agree there is subjectivity and trade offs at about every | turn. In fact, the only thing I'm certain of is that Go | missed what I would look for in just about every category | except a few (but I agree it is easy to learn, but does it | matter if you can't write good code with it?). That said, | very intelligent and respected people disagree with me, so to | each their own. | leshow wrote: | > I like Rust probably... (even if obviously not finished | yet). | | what? what languages fit your definition of finished? | onei wrote: | My disappointment with Rust, that in no way diminishes | how much I enjoy using it in my own time, is that I have | a hard time recommending it for microservices, which are | arguably an average project at an average company. The | ecosystem just doesn't feel as fleshed out or complete as | in Go. It's a shame because there's libraries in Rust | that I adore like clap, serde, and diesel but when I last | wanted to write something that integrates with AWS, I | found a deprecated unofficial crate and a non-production- | ready official crate from AWS. | | I don't know whether to attribute this disappointment to | the breadth of what Rust can be used for and the | difficulty in doing all of them well, or a lack of | wider/corporate buy-in for these use cases. It's a pity, | because after getting past the initial learning curve I | struggle to find anything wrong with the language itself. | cpeterso wrote: | What tools or libraries is Rust missing for building | microservices? | foolfoolz wrote: | > It makes it easy for any random kid to dive into an | existing code base and become productive, that can be super | valuable | | professional developers are rarely "kids" | erik_seaberg wrote: | > mundane, repetitive, boring language | | These apply just as well to assembly, yet they are the | reasons almost nobody tries to use it. | uncomputation wrote: | > I'm positive linguists aren't happy with English becoming | the global language either | | Nit: Linguists do not hold prescriptive opinions about the | "quality" of languages. They analyze languages to form | descriptive theories about the structures and features of | certain languages. I only nitpick this because, while having | a lingua franca is certainly valuable and there is nothing | "bad" about English any more than literally any other | language, the closest thing to this in programming | "languages"/notations is actually C, not Go. It's hard to | overstate the invisible influence on C on basically every | mainstream language and how we think of programming and | computers in general. | | An interesting analogue to your example of English is the | varying efforts to transliterate most languages into using | the Latin alphabet, much like how many programming languages | today need/greatly benefit from a compatibility layer with a | C compiler/the C standard library. | halfmatthalfcat wrote: | Having written a lot of Scala, I generally agree until you | can form a team that knows what they're doing or has a | background in Scala. After that, Scala shines as the | codebase(s) scale. | ttfkam wrote: | Substitute Scala for any other programming language and the | statement still holds. | | Experienced developers and those with specific language | expertise excel when implementing in that language. | | Who would have expected that? /s | halfmatthalfcat wrote: | The point is the barrier to entry for Scala is higher but | once you have a skilled team working in Scala, you | probably get more velocity than others due to the | language ergonomics. | monkey26 wrote: | You sum up my 3-4 years writing in Go. I'm going to have to | point people to this comment. Thanks. | matthewmacleod wrote: | Yeah, I think your view here is pretty close to the way I feel. | | It's unfortunate, because I think Go generally gets _a lot_ of | stuff really right. But it too often feels that it prizes | simplicity in implementation or specification over simplicity | of how code is read and written. | | Writing Go feels like having a little rock in my shoe. I think | it comes from that situation where you know some property of | the code you are writing (e.g. "this thing cannot be null"), | but there is no method to assert this to the computer. And so | instead of my very fast and logical computer with lots of | memory enforcing this for me, I have to carry that knowledge | around in my own lossy memory as a little bit of baggage and | hope I never put it down and forget about it. And I find that | this comes up in Go all the time. | | That sucks because Go is still--despite all this--probably the | best overall solution for the kind of apps I end up writing a | lot. I really do think that there's still a space in there for | a language sitting on the complexity scale between Go and | others around the level of Java/Swift/Typescript, which can | benefit from both some excellent design decisions of the | former, and some of the expressiveness- and correctness- | enhancing features of the latter. | dap wrote: | It's a funny thing about simplicity. It's easy to make | something simple by foisting the complexity on the consumer. A | lot of Go's features are simple in that they take few words to | explain, but using them uncovers so many edge cases that they | wind up being quite complex. | tptacek wrote: | You say "a lot". What's your third example? | fpoling wrote: | The sad part of lack of sum types in Go is that the select | operator is kind of a typical sum type operation to pick up one | of sum type branches. So the language has this notion, but it | is extremely limited. | throwaway894345 wrote: | This is one of the warts I wish Go would fix (implementing | Rust-like enums, and ideally getting rid of zero values and | nils, but these things won't happen). Even still, Go is the | most productive language I've used because it turns out type- | systems (cool though they are) are overrated--you only _need_ | enough of a type system to keep things documented for humans | and tools. 95% type safety seems to be the sweet spot (peak | productivity) after which productivity begins to rapidly | diminish. It's more important to have decent performance, | good tooling (simple with sane defaults), small learning | curve, great deployment story, etc. | ttfkam wrote: | I have never seen a language successfully eliminate | nil/null and zero values after the fact. Once they're in, | they're there to stay. | nu11ptr wrote: | Yep, and multiple return types are like tuples, minus the | ability to compose them and actually use them as a single | type. Map and lists were generic, but a special kind the user | couldn't make (fixed now I think w/ generics in 1.18). | 'range' worked over a magic iterator, but not one you could | ever make. Their "enums" are variable bindings minus the | ability to be sum types or any other decent property of an | enum. Nil is nothing more than "None" or "Empty" in an Option | type, but since they didn't use the type system you can get a | classic null pointer exception. Their "attributes" are just | comments, but now you need to remember their special | formatting since each one is effectively arbitrary text. | | In so many instances, they made things "special" to avoid | bringing in a concept, but you have to learn that concept | anyway, but now as a special case. It is almost as if they | said "we can have 5 features and I don't care what #6 | does..it is out...we must make a language with 5 features!". | Instead of saying: "What is a reasonable set of features that | compose well, are logical, expressive, and relatively simple | such that people can both easily learn and scale their code | bases". | | A great example is Brainfuck. Everyone would agree it is | 'simple' and it only has 8 constructs, but it is not 'easy' | to write programs in. 'simple' is not the right metric for a | language. | typesanitizer wrote: | > I don't think it focused enough on the positives (of which I | think there is some - for example, it has very nice and highly | performant lightweight threads with full blocking semantics via | goroutines). | | I've tried to focus on things that I ran into. I haven't had | the chance to write a lot of concurrent code in Go, that's why | I didn't comment on this aspect. Most of the code I've been | working on has been serial, with concurrency and parallelism | managed at a different layer than the one I was working on. | | That said, the same point applies for a bunch of negative | points that you may seen in other articles criticizing Go. I | didn't mention them because I didn't run into them in practice. | twblalock wrote: | > I like the idea of a simple language, but not the choices | they made. | | That's the problem with simplicity, and it's why kitchen-sink | languages like Java will always be popular. | | People complain about the complexity of Microsoft Office too -- | they only use a subset of the features. Why does it need to be | so complicated? Because every customer uses a different subset | of the features, and what you end up with is the combined | superset of what everyone wants. | omginternets wrote: | >The loop iteration variable is reused across iterations, so | capturing it by reference (the default for closures) is likely to | lead to bugs. | | >defer inside a block executes not at the end of the block, but | at the end of the enclosing function. | | >defer evaluates sub-expressions eagerly. | | Whenever I see this kind of complaint, I can't shake the feeling | that the author struggles to distinguish between "bad design" and | "learning how things work". There are many fair criticisms to | levy against Go, but "it didn't behave as I first expected" is | hardly one of them. If it were, there would be lots more to | complain about in the languages that are routinely touted as | superior to Go, namely: Rust and Haskell. | nathants wrote: | what sold me on go: | | - go build. | | what keeps me on go: | | - gopls + errcheck + ineffassign. | zozbot234 wrote: | Take note everyone, this is how you do well-argued criticism of a | programming language or a technology more generally. Much more | polite and better written than what we recently saw here at HN | re: the same topic. | WYepQ4dNnG wrote: | I wish an expressive language like Rust, but with a GC, so I | don't have to think about borrow/checker and memory manager in | general. | | I have tried to use Go but did not find appealing or ergonomic. I | would choose Java/Kotlin over it, perhaps with GraalVM. | | If I had to write system tools, I'd probable go with Rust. | zozbot234 wrote: | > I wish an expressive language like Rust, but with a GC | | Ocaml or ReasonML are pretty close to that. | pjmlp wrote: | And F#. | pizza234 wrote: | I've recently written a "system tool" in Rust, and, while it | wasn't as bad, in terms of productivity, as I had imagined at | first, there are certain domains where Rust is really a grind. | | Working with filenames/paths is one of those - if one works | with (transforms) filenames/paths, the code will be polluted | with all the conversions between PathBuf/Path/OsString/OsStr | (and the canonical String/&str); this makes it hard to reason | about the abstract logic. | | It absolutely makes sense that Rust forces one to consider the | robustness of the code, but in some cases one just doesn't want | such robustness. | | Cyclic graphs are another very ugly thing to work with in Rust | (without supporting libraries). | ______-_-______ wrote: | If you don't care about robustness for paths with invalid | utf-8, you could try camino https://crates.io/crates/camino | pizza234 wrote: | Very interesting! I'll actually check out if it fits my | project. Thanks! | shintakezou wrote: | I "love" computer language criticisms, even though I don't | believe neither that the perfect language can exist nor that a | language can fit all the needs. Anyhow, here I didn't get what's | wrong with some of the "issue". For example: you can't get the | address of a literal, but you can take the address of a variable. | This isn't surprising. And about arrays/slices/maps, append and | make: it seems to me fairly logical if you don't disregard what | they are. (If I've got the issue, and I am not sure about the | exact point.) | warent wrote: | In the recent Go critiques posted in HN I got really annoyed | because the articles came across as snide and such-and-such | miscellaneous things (that I expressed in a somewhat poor, | reactionary way last time) | | In my humble opinion this article is very well written. While it | leans more negatively and is a critique, you're being very | professional and respectful. I appreciate your write-up and | perspective. Some of these issues flow into problems I've also | had with Go, particularly in trying to use it for a GraphQL API | (which was a horrible idea!). Feeling cautiously optimistic about | generics :) | kitd wrote: | _Cannot make a type in a foreign package implement an interface_ | | The idiomatic way of adding an interface to a foreign type is to | declare your own local alias and use that to implement the | interface. At least then the added functionality is confined to a | known package and won't lead to surprising behaviour elsewhere. | everybodyknows wrote: | > It's not clear if a value that is passed via pointer is | intended to be mutated or not. (const-ness / mutability) (maybe | it's passed via pointer just because the size of the struct is | large?) | | The dilemma is even richer than that -- what if the function call | is inlined, with the struct known to be instantiated in the | caller? If so, size of the struct doesn't matter. In C, inlining | can be forced, so passing a struct by value can be assured to be | economical. The programmer has no such option in Go. | tschellenbach wrote: | Go keeps it core feature set very minimal by design. The end | result is a language that is almost as fast as C++ and almost as | productive to write as Python. If you care about performance, but | want to work with a small engineering team it doesn't get better. | | Sure you could use Rust to be a tiny bit faster in some use | cases. But it's also less productive to work with for your team. | You could use Python, it's a bit more productive but performance | is 40x slower. | | The value of Go is the balance it strikes. | | That being said, yes a good enum type in the language, ideally | like Kotlin's approach would be ideal. | olah_1 wrote: | > If you care about performance, but want to work with a small | engineering team it doesn't get better. | | What about Zig? I guess people don't like the lack of libraries | there? | pclmulqdq wrote: | "Almost as fast as C++" claims about go tend to come from | people who don't write a lot of C++. The perf gap is big, even | though it's not as big as the perf gap with Javascript. That | said, I don't think anything but Rust does any better than Go | on getting the balance right. | [deleted] | GlitchMr wrote: | > `filepath.Clean()` is not called `filepath.Canonicalize()` | | Canonicalization of a path is a different operation, as a name | suggests it returns a canonical path to a resource. The idea | being that if you happen to have two paths that refer to the same | file (say, `/bin/sh` and `/usr/bin/sh`) then you should be able | to pass those to path canonicalization function to get the same | string for both. This is not what `filepath.Clean` does, so | calling it `Canonicalize` would be confusing. | | For example, in Rust the following program when run in Rust | playground (https://play.rust- | lang.org/?version=stable&mode=debug&editio...) will output | /usr/bin/dash: fn main() -> std::io::Result<()> | { println!("{}", | std::fs::canonicalize("/bin/sh")?.display()); Ok(()) | } | | Meanwhile, Go's `Clean` function is only concerned about lexical | processing and ignores the file system entirely and would return | `/bin/sh` here, as none of rules `Clean` uses apply. | typesanitizer wrote: | This is a fair point. To be clear, I wasn't suggesting | Canonicalize as _the_ name, but as one potential candidate. As | you've shown, it has some shortcomings. Perhaps Normalize is a | better alternative (and also another candidate that I suggest), | since "Normalization" is a commonly used term for converting to | a standard/normal form. | cpeterso wrote: | People address some of these issues piecemeal with linters and | code generators, but maybe there's an opportunity for someone to | create a Go++ language that accepts Go syntax but also extends it | with sum types, nullability annotations, no default values, | checked error handling, operator overloading for collection, etc. | I remember people were excited (and dubious) of the Go-like | language V. | alexchamberlain wrote: | > The loop iteration variable is reused across iterations, so | capturing it by reference (the default for closures) is likely to | lead to bugs. | | Is there a language where this isn't true? | masklinn wrote: | > Is there a language where this isn't true? | | Java[0] and C#'s foreaches, Rust I think[1], Javascript's | `for...of` when you use `let` or `const`. Probably a bunch of | others, this is just off the top of my head (edit: just | checked, Swift as well) | | And obviously languages which do away with "imperative" | iteration entirely e.g. erlang, haskell, clojure, ... | | And it should be noted that this is more problematic in Go than | in most, because of Goroutines (if you create goroutines using | closures in a loop you're hitting this issue). Javascript was | extremely hard hit by that (because of closure-based async | stuff, and also that `var` is even worse) for similar reasons, | which is what led to to `let` and `const` having so much better | scoping. | | Incidentally, I assume that's at least one of the reasons why | the order of evaluation of the `go` statement is so weird. And | why you probably should not use anonymous functions to create | goroutines. | | [0] also Java doesn't allow closing over variables which are | not effectively final, so the issue couldn't happen, if foreach | variables were not effectively final the compiler would reject | the closure | | [1] though the borrow checker will usually tell you to get bent | before you can even run the code | kgeist wrote: | C# since version 5.0 | mwcampbell wrote: | > Go has a convention that doc comments must begin with the name | of the entity they describe. | | Does anyone know the rationale for this one? My only guess is | that the other common style, where the doc comment starts with a | verb, allows for variation in how that opening verb is | conjugated, e.g. "Decompress the tarball" versus "Decompresses | the tarball". Maybe the Go team figured they'd eliminate that | ambiguity by establishing a convention that the doc comment | always starts with a complete sentence with an explicit subject. | endorphine wrote: | I think they refer to the following suggestion: | | > Doc comments work best as complete sentences, which allow a | wide variety of automated presentations. The first sentence | should be a one-sentence summary that starts with the name | being declared. [...] If every doc comment begins with the name | of the item it describes, you can use the doc subcommand of the | go tool and run the output through grep. Imagine you couldn't | remember the name "Compile" but were looking for the parsing | function for regular expressions, so you ran the command [...] | | source: https://go.dev/doc/effective_go#commentary | h1fra wrote: | I think this very specific details is what summarize to me the | pain of using go. | | Everything is a "convention" but nothing is enforced in a way | that would make it easy for developer. There is actually | nothing preventing you to not write doc comments like that, | nothing to enforce err check, to put interface{} everywhere, to | assign nil to a pointer, etc... | | By design the language is actually super loose and it heavily | contradicts the goal of the language itself. When they say it | was for junior/quick onboarding I have nervous laughs, I never | saw that much coding mistakes happening to senior devs than in | a Go codebase. | | You only follow standards if you use golangci which is not even | an official tool. | tptacek wrote: | It's a weird call-out in the post, especially since their | example is a non-exported function. | typesanitizer wrote: | Ah, making it non-exported was an unintentional mistake. I've | pushed a fix marking it as exported with an EDIT note. | tptacek wrote: | For the substantive critique: it's also a bit weird because | it's pretty clear what the reason for it is: it's a | simplifying convention, like the capital-letters-to-export | thing (which I'm partial to, since it's a convention I use | in my C code). It seems reasonable to have preferences in | the other direction, but (and I'm not trying to argue that | you wrote it this way) not to suggest that it's somehow a | flaw in the language. | oxplot wrote: | > Errors on unused variables are annoying as well. | | > Not everything that should be done needs to be done right here | right now. | | Hence the ability to suppress it with `_ = unused_var`! The point | is that it's explicit. You have an out -- it's not the pretty out | you like but that's the whole point. It's unpretty and annoying | in order to force you to think twice. Every design decision is Go | has been the result of consensus of multiple people who've been | bitten many times by the issue the design is addressing (in case | that wasn't obvious). Have a read of issues, drafts and proposals | on Go issue tracker to get a feel of what it takes to get | something in. | | > Imagine trying to learn a musical instrument and being berated | at every time you play the wrong note. That's not a way to teach; | it's a way of asserting dominance. | | WTF! | | > No sum types with exhaustive pattern matching | | > I tried a search for a code pattern often seen due to the lack | of exhaustive pattern-matching in Go | | > That's 38.7k hits in the source code across GitHub etc. as of | Apr 29 2022. | | Great. Write it up in an issue and with such an overwhelming | evidence, it's a good candidate to make it to the language in the | future. Why isn't it there to begin with? See above. | | > No overloading for common operations | | For many, that's a huge positive. When reading the code, it's | much easier to reason about the extent of side effects for-loops | have. | | > Yes, one can use map[T]struct{}, but that feels needlessly | cumbersome. | | Now you're just trying too hard. | | > Go has a convention that doc comments must begin with the name | of the entity they describe. | | Rob Pike, and docs, and blog posts talk about why this is the | convention. Read up. | | > Limited markup support in godoc | | Thank goodness. | | You have a rust/swift programmer (from dozens of mentions of each | language) trying to beat Go into a shape they're familiar with | and not enjoying it. That's not the right way to learn and use a | language, just as you don't go learning Japanese, expecting it | follow the grammar rules of English. And especially in case of | Go, bits are added pragmatically and after long and careful | deliberation, not as a race to have as many features, bells and | whistle as every other hot-lang out there. | | A experience report that keeps mentioned language X and Y as a | benchmark for what language Z should be like, only tells how | language Z differs, not whether it's better, worse, etc. So | labeling the differences positive and negative is of little value | in this case. | Karrot_Kream wrote: | Look, it sucks when programming languages get condescendingly | bashed flamily, but the OP did none of the sort here. I, | personally, don't find things like the map-as-set syntax | cumbersome, but I can see the argument from the OP. And OP also | freely admits they have 6 months of experience with the | language, enough to have the weird bits stick out, but not long | enough to have internalized them. I don't think all of these | are "trying to beat Go into a shape they're familiar with", I | think they're legitimate complaints about the language. I don't | find them problems myself, but I think it's good to document | issues and pain points so that future language | developments/languages try to incorporate this feedback. | eikenberry wrote: | No mention of CSP makes me think the author is still writing Go | programs in a non-concurrent, imperative style. Used in this | fashion you only get limited gains from Go's simple syntax and | nice tooling. But writing using CSP patterns gets you a different | experience and feel and is where Go really shines. | halfmatthalfcat wrote: | Go's concurrency story isn't great compared to other languages | tbh. Combining channels, wait groups and mutexes (because | sometimes you do need to use all three or mix/match) leads to | confusion on when exactly to use what, when. | tptacek wrote: | It's more or less the same as the story with Tokio programs | in Rust; they just spell the word "channel" differently. | Karrot_Kream wrote: | I always find myself reaching for the same constructs in | other languages. In Rust I go with crossbeam channels and use | mutexes or RwLocks when I don't need the channel | synchronization overhead. | fpoling wrote: | Ada had only CSP at the start of eighties and then gained | mutexes to allow for more expressive and efficient patterns. | | It is very puzzling for me why Go went with CSP and added a lot | of language support for it like the select operator. A data | type like a priority queue to post messages to a thread would | serve similar purposes as CSP while allowing for more patterns | and simpler reasoning. | evmar wrote: | As a person who likes Go a lot, and who finds Go bashing -- and | really, bashing in general -- pretty tiresome to read, I thought | this was a pretty decent article and the sort of thing we ought | to encourage on HN. At least in contrast to the usual rants. | | In particular I appreciated the author's interest in exploring | the reasoning behind the design decisions they disagreed with, | rather than stopping at "I don't like X". | typesanitizer wrote: | Thanks! I'm a fan of your work and posts on Ninja, so I'm glad | to hear this. :) | masklinn wrote: | > This seems even more strange; instead of giving a "missing type | in composite literal" error, it gives a syntax error. | | That's a syntactic limitation (wilful I assume): `{1, 2}` is not | a valid expression in general, however it is specifically allowed | as an item within an existing composite literal: | CompositeLit = LiteralType LiteralValue . LiteralType | = StructType | ArrayType | "[" "..." "]" ElementType | | SliceType | MapType | TypeName . LiteralValue = "{" [ | ElementList [ "," ] ] "}" . ElementList = KeyedElement | { "," KeyedElement } . KeyedElement = [ Key ":" ] | Element . Element = Expression | LiteralValue . | | The "LiteralValue" item occurs _only_ as a sub-item of the | CompositeLit rule, which means it 's not valid as a function | parameter (or as a value to set on a variable, or as a return | value). | typesanitizer wrote: | If you look at the GopherCon talk I gave (linked at the | beginning of the post), it is about reading the spec. So yes, I | did read the spec, and I realize that this is not syntactically | valid (it would be a very basic compiler bug if this were valid | syntax and it was diagnosed as a syntax error). | | However, the spec only states what _is_ but not _why_ it is | that way. Sure, I could look at the git blame of the spec for | every odd thing I run into, but there is only so much time in | the day... | masklinn wrote: | > However, the spec only states what _is_ but not _why_ it is | that way. Sure, I could look at the git blame of the spec for | every odd thing I run into, but there is only so much time in | the day... | | Parsing simplicity and disambiguity (and thus speed) is a | pretty obvious reason: if you allow `{}` to be an expression, | then you have to look ahead any time you encounter an | unprefixed `{` to try and figure out whether it's an | expression or a block opening brace. Or you have to make your | grammar into an unholy mess such that {} is an Expression but | _not_ an ExpressionStmt. ___________________________________________________________________ (page generated 2022-04-30 23:00 UTC)