1. 35
    1. 34

      I don’t have anything knowledgeable to put in this debate - I don’t use go - but the language used by Pike in the quote was a bit painful for me. The whole quote is:

      “The key point here is 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 use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.”

      “They’re not capable of understanding a brilliant language” - um, I don’t know. Is this a good management style, dissing your own employees? Also stereotyping them? Lots of brilliant people I know aren’t “researchers” i.e. don’t have PhDs or papers. They can work through stuff just fine.

      “but we want to use them to build good software” - uh, very “human resources”. Perhaps more “we want them to be able to build good software”?

      It could be a turn of phrase but “using” people seems a bit like thinking of people as simple machines. Again, leaves me with a funny feeling.

      Words expose a lot of how we think. I don’t like this kind of thinking.

      1. 7

        Probably he is just being sarcastic on some languages when he said “They’re not capable of understanding a brilliant language”

        Personally I don’t see any problem with word “using” I sometime use that for myself. When I feel management is not using me to best advantage for company/product, I just say “You are not using me” that doesn’t mean I’m considering myself machine. (arguably we are just brilliant machines evolution have made)

        Don’t be so sensitive, people are wired differently from each other.

        1. 15

          If you are prepared to accept that people are different from each other, then don’t tell people not to be so sensitive. Accept their sensitivity.

          1. [Comment removed by author]

            1. 8

              Sensitivity is not a thing people can, or would usually want, to turn off. Why would you want to be numb to this perception? Discomfort can be one of the most motivating, educational and ultimately beneficial forces imaginable. In this case, listening to it may help kghose avoid unfavorable future situations.

      2. 2

        Sort of bums me out to hear someone’s worst-sounding quote treated as the “real” them. I expect it more in, say, politics, but it’s not necessarily great there either.

        I think there’s a less upsetting but related argument for simpler languages, though I’m not sure if it’s actually what Pike was getting at. Kernighan and Plauger said, “Everyone knows that debugging is twice as hard as writing a program in the first place. So if you’re as clever as you can be when you write it, how will you ever debug it?”

        When I started working as a programmer, I was, if anything, smarter than today. I did all sorts of tricks to make code shorter (especially), get things done faster, and try and eke out better performance. (Code that smooshes together other code, my own little languages, more.)

        I wound up building some messes that were hard to maintain, using clever approaches that worked at the time but were hard to maintain later, especially when more coworkers entered the picture. I can’t exactly fault past-me; to start with, it was hard to foresee what’d be tough to maintain in, say, three years before actually working three years.

        An environment that didn’t make some of those clever options so readily available could have served my clients better by leaving the code more understandable, and even now I don’t mind worrying less in Go about choosing from several ways to express the same result (Jeff Hodges and Rob Napier get me on the latter thing, I think).

        This is all just the bones of a discussion that needs much more nuance and fleshing-out. I’m not saying everyone new to the industry makes the same mistakes I did, or whatever, and I also directly feel the costs of the status quo (my sorting package could really benefit from something generics-y!). I’m mostly trying to say that smart coders can also sometimes benefit from more Spartan environments, especially in maintainability.

        Also, for all their apparent grumpiness, Gophers haven’t ruled generics out: team members acknowledge the pain, but they’re not high on the to-do list because of the complexity.

        If any Go users want to move the ball on this stuff, I think rolling your own Go tooling on top of the existing language is an underrated approach. (Beyang Liu had a neat talk on this.) There’s plenty of neat parts to build on (go/parser, ast, etc.), some tools get pretty popular (goimports, errcheck, gocode), and some do pretty wild things (gotgo, revel). It’d be interesting to look at something like bradfitz/slice or an algorithms library (even encoding/binary) and ask, “can we deploy codegen [or whatever] to make it on net saner to write and use stuff like this?” It’s only one of many interesting areas to explore and the focus it gets sometimes seems disproportionate, but doesn’t change that it’s interesting.

    2. [Comment removed by author]

      1. 3

        The nice, static binary is one of the most compelling things for what I’m working on, but damn I actually miss Java.

        Would OCaml be an option?

        1. [Comment removed by author]

    3. 8

      I really enjoyed this post! Balanced and thoughtful critique of the language.

      I use Go quite a bit at work, and I really, really like it. Writing a server app is super easy, fast and fun. With static typing, these days I prefer to use Go for hacky scripts instead of Python.

      But, the blogpost author is spot on about the community. Too many are defensive and dismissive of comments and concerns raised by users. Sure, you may not have figured out the best way to do generics, but at least have it as a goal somewhere in your roadmap. I see things like algebraic data types and type traits in languages like Rust and I think to myself, wouldn’t it be great if I could use those in Go. Hopefully, things will change, because as author points out, Go has a lot of good things going for it. Don’t let complacency kill the language.

      1. 4

        But, the blogpost author is spot on about the community. Too many are defensive and dismissive of comments and concerns raised by users.

        It would be nice if they weren’t defensive about it, but can you seriously blame them? Go has been getting crap for lacking generics for a very long time. It just gets tiring after a while. It’s the same trope over and over. (I’m not saying the criticism shouldn’t be made, but at the same time, we should understand the other side of things.)

        Don’t let complacency kill the language.

        I really don’t think this is about complacency. It’s about a concerted effort to say No to additional features. It seems people either love or hate this policy.

      2. 2

        Too many are defensive and dismissive of comments and concerns raised by users. Sure, you may not have figured out the best way to do generics, but at least have it as a goal somewhere in your roadmap.

        I agree.

        I see things like algebraic data types and type traits in languages like Rust and I think to myself, wouldn’t it be great if I could use those in Go.

        A lot of people think that some form of generics can be added to Go at some point. But I’m less optimistic with ADT. They were some discussions about this on golang-nuts and the general conclusion was that it would very difficult to introduce ADT without a large change in the language. ADT would interact in a lot of ways with fundamental features of the language like zero values and the way variable are initialized.

      3. 2

        Stability isn’t complacency. There is a value to letting the community build on top of a stable base, while that base is made better. If you want features and constant language development – look to Rust. I don’t think Go should try to be Rust – nor vice versa.

    4. 14

      Stop discussing and write some code, guys! The world needs it! Use Rust, Go, Python, w/e. I personally favor the Lobsters articles which feature achievements and advances rather than dozens of rants on a daily basis. The language used in the process is more or less irrelevant when it fits the raw needs as a tool for the job.

    5. 6

      I have felt like Go gives me the hacking speed of Python with closer to the runtime speed of a compiled language. I feel like the “understanding a brilliant language” thing is a great commentary. If you need to interface 1 or more of the standards, technologies, what-have-you in the standard library: https://golang.org/pkg/ Go is going to serve you well and the code will most likely still be legible in a few months by someone who isn’t an expert. The documentation is extremely straightforward reminiscent of the Harbison & Steele C standard book. If you know your problem domain, you can find your answer damn quick.

      https://golang.org/pkg/unicode/ compared to the Java, Clojure, Rust (marked experimental), C++ (honestly I don’t know how C++11 is handling unicode officially now), Python (well 2.7 3.x? I use it constantly but with pain), Haskell w/ self documenting gems like (guessEncoding :: String -> String), Nim seems semi-ok-ish, Erlang has pretty clear docs but I’d love to see someone who can google <language name> unicode and find a more usable library docset.

      Documentation let’s me know if I can find what I need to know about something I don’t know without asking questions or searching the web.

      What other languages come close to that level of documentation for their standard library and single source clarity? Most have been diluted by implementations / history. Unicode isn’t the fairest comparison, but… this was an off hand comment I got carried away with.

      1. 5

        librustc_unicode is internal only, because it uses unstable features and declares language items. All Rust Strings and &strings are UTF-8, and https://crates.io/search?q=unicode has a bunch of different things to help you with other various aspects.

        1. 2

          I appreciate this comment a lot! I think the single source documentation of certain projects endears them to me from a pragmatic standpoint. I’ll admit that I haven’t done any of the tutorials for rust and I don’t know much about it. Congrats on reaching 1.0 and hopefully making the world a bit safer. Very interested to see the http://doc.rust-lang.org/stable/book/README.html that you’re working on.

          1. 2

            No problem, and thanks :)

    6. 5

      Good summary of pros and cons. My additional pet peeve is the problem of accidentally mutating a temporary copy - but it seems to be only me who suffers from this so I repeat the peeve here hoping someone will educate me away from it.

      General problem - in a couple of cases, golang makes a temporary copy of an object. The cases I have found problematic are in the “for _, obj := range something { obj.Frob() }” and to a lesser extent when I have a non-pointer receiver for a method. I guess I’m used to pass-by-value for function args and this case is not problematic to me.

      My problem is that there is no warning if Frob() is a method which mutates obj. If it is, then the mutation occurs on a temporary copy which is then discarded.

      The reason this is problematic for me is that maintenance changes which seem benign can fail with no error or warning.

      Real world example: an accessor method which computes a value based on an object’s state. Profiling shows the computation is mildly expensive and called often. Reasonable solution is memoise the result of the computation inside the object. The most obvious and way to do this involves changing a method from a value receiver to a ptr receiver, so I can store the memoised value in the object.

      Then re-profile and discover that this doesn’t provide a speedup. Then curse when I recall that most of the access to the object are via a for … range loop like the above, so the memoised value is thrown away.

      The problem for me is that a seemingly-reasonable change (change a method from value receiver to ptr receiver, and do some mutation) caused this problem. At the moment, it seems that such a change requires analysis of all callers to work out which method calls are on temporaries. Effectively, it’s a complete API breakage. I’d like API breakage to be flagged up to me by the compiler.

      I’d be interested in either or both of the following changes:

      1) have the _, obj := range objlist syntax alias obj to the array element (or equivalently, return a ptr to it). This is done, for example, by perl ‘foreach my $x (@foo)’. Perhaps also outlaw value receivers for methods.

      2) some kind of inferred const/non-const detection which can error if I’m calling a non-const method on a temp object about to be immediately discarded.

      At the moment, it seems to me that the best way to get sensible semantics everywhere is to always use pointer-to-obj everywhere. Is that what everybody else does?

      At the moment, there seems to be a tension in the language between “should I use a value or ptr receiver”, “should I have a map to values or ptrs-to-values” and a blurring of the two (golang will compile x.Foo() happily if x has a value receiver or ptr receiver).

      1. 2

        It is interesting, that is a bit of a blemish that I simple don’t even think about anymore, I naturally go to the

        for k, v := range Vs { if v.Thingee { Vs[k].Frob() } }

        route now.

    7. 4

      Nice solid post @tylertreat. As a semi-frequent writer of Go code, it really resonated with me.

      1. 3

        Thanks. Honestly, I was on the fence about publishing this at all. As I was writing it, it was starting to feel ranty, and I normally try to hold myself to a highish standard. I know it’s going to catch a lot of flack, and there are a lot of similar posts out there. Go is a bit of a love-hate relationship for me. shrug

    8. 4

      The quote I like is “Go makes it easy to write code that is understandable. There’s no “magic” like many enterprise Java frameworks and none of the cute tricks you’ll find in most Python or Ruby codebases.”

    9. 5

      A copy of the comment I posted on the author’s blog

      I agree that generics are very useful when writing reusable libraries, but I disagree they are absolutely necessary when writing and maintaining software at large scale. At large scale, at some point, processes communicate with other processes on the same machine or over the network, and you have to marshal/unmarshal and type check at runtime.

      Your example with the function isInt64InSlice is a good illustration of why the lack of generics is annoying at small scale, but it hardly shows why this is a problem at large scale.

      PostgreSQL is an example of a complex piece of software, written in C, which doesn’t use generics and is still very readable and maintainable.

      A few other technical points:

      • “Cannot add methods to types from a different package”: Would you want something like extension methods in C#?
      • “Channels are slow”: I’ve read there is some work going on to improve them. That said, you admit yourself that Go is still one the best option among the “concurrent” languages. Are you aware of another language with a similar mechanism builtin in the language or the library and that performs better on this matter?
      • “Accidental implementation of an interface”: I agree this a problem in theory, but in practice it’s very rarely an issue. Have you been beaten by that often? Do you think the benefits of implicit implementation are not worth it?
      • “Cannot implement your own exceptions”: Yes you can. You can pass some value to panic and retrieve it upper in the call stack with recover and decide what to do depending on the value. It’s not something Go developers do often, but there is some use of this technique in the standard library for example. It’s explained in “Effective Go”.
      • “Map returns a bool instead of an error”: I don’t see the issue here. A lot of other languages do the same and return a default value and/or a boolean at false when you probe a map with a missing value.
      • “Adding an item to a closed channel panics”: This is a programming error, like a division by zero or an out of bounds array access.
      • “Channels as iterators”: Goroutines and channels are not designed to build iterators. They offer a general low-level mechanism for concurrency. How would you expect the goroutine to know that nobody is listening anymore and shutdown?

      About the community, I would not call it “stubborn”. In my experience, it is generally helpful and very professional. But I agree that the topic of generics has become a taboo, which I regret. It looks like the core team wants to focus on improving the compiler, the runtime, the library, the tooling and the garbage collector, and because of that wants to keep the language stable in the meantime. I understand the frustration, but don’t you think their decision makes sense and is the best way to use the limited resources of the core team?

      You wrote that “a language can have considerable depth while still retaining its simplicity”. What languages would you recommend that solve the flaws of Go while preserving its simplicity (in terms of user experience)? It’s a sincere question. I’d like to use such a language.

      More generally, you ask how can we make developers more productive and how can we enable them to solve problems? That’s interesting because this is precisely the question Go tries to answer. Are you sure the answer lies in a more powerful programming language? What about the runtime, the tooling, the libraries?

      The Go team thinks the answer lies in simplicity (light syntax, garbage collection, interfaces, composition over inheritance, builtin arrays/slices/maps), builtin concurrency (goroutines, channels), great tooling (speed of compilation, go test, gofmt, godoc), easy deployment (no virtual machine, static binary).

      Do you think the answer lies in having type parametricity, algebraic data types, pattern matching, immutability, Hindley-Milner type inference, higher kinder types, etc.? I’m not saying they are not useful or desirable. I’m just saying that the low hanging fruits in terms of developer productivity may be elsewhere.

    10. 3

      Fantastic article @tylertreat. This very closely echoes my own experiences with Go, though ultimately the experience led me to put Go to the side. I spent a long time (too long) in a frustrating love/hate relationship with it. Thanks for writing this up—I’ll be linking to this often, I think.

    11. 3

      Really nice read; this is exactly the reason why “we” are now actively using Go for our internal tooling and - since two weeks - are building some backend server stuff we used to build in Ruby in Go. Gave a short workshop three weeks ago introducing the language, the basic constructs and explaining the ecosystem and at least 3 developers here are now actively using it on a daily basis.

      It’s a stupid language that actually makes it ‘semi-hard’ to do clever stuff.

    12. 1

      Why can’t C++ compilation be as fast as go ? I don’t understand if it’s the compiler’s fault or the language’s fault.

      1. 5

        It’s the language’s fault. A good explanation of this is in the article “Go at Google: Language Design in the Service of Software Engineering”, section Dependencies in C and C++

      2. 1

        Recursive #include.

    13. 1

      Further, you can’t add methods to types from a different (or standard library) package. Instead, you must effectively alias or wrap the type with a new type, resulting in more boilerplate and code that generally takes longer to grok.

      I find this to be one of Go’s biggest strengths. I wrote about why in this post.

    14. [Comment removed by author]