[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)