Hacker News new | comments | show | ask | jobs | submit login
“Go’s design is a disservice to intelligent programmers” (nomad.so)
131 points by apta 941 days ago | hide | past | web | 160 comments | favorite



My summary of the author's points: no generics, inexpressive, no good package manager, procedural.

Of these, caring about the fact that it is procedural seems pure opinion. Lacking a package manager is not really a language issue (PHP's package manager, for example, is not coupled to the core language).

So, we are left with the lack of generics and the lack of expressivity. I'm not deep enough in the weeds to be able to argue pro/con for generics intelligently right now, so I will concede that as a concern that has been raised by many.

The lack of expressivity seems to be an inexorable consequence of the goal of simplicity, so I'm sympathetic. That said, it seems to be a tradeoff acknowledged by Go's authors, not an oversight.

Overall, these points don't convince me of the author's thesis (or, at least, they don't seem to justify the title's degree of inflammation).


I hadn't really looked at Go before, but I have to say the generics issue reminds me of Java 10 years ago before it got generics. Java also had the goal of being simple, which is probably why it's so popular, but for me it was always too simple in the sense used in TFA (haven't tried Java8 yet though.) At least it looks like Go has higher order functions, but I don't see how they will be that useful without generics unless you subvert the type system.

I definitely think the too-simple nature of Java is the reason behind all the reams of boilerplate Java code found in most any Java project, and I can't see how Go would be any different. It seems like a shame not to have learned that lesson.


I think the too-simpleness of Java 1 also has made Java 8 more complex than it could be. Java got generics 'the easy way' through reification, and that gave us the mess that is mentioned in http://java.dzone.com/articles/whats-wrong-java-8-part-ii:

"the methods of these interfaces have different names. Object functions have a method named apply, where methods returning numeric primitives have method name applyAsInt, applyAsLong, or applyAsDouble. Functions returning boolean have a method called test, and suppliers have methods called get, or getAsInt, getAsLong, getAsDouble, or getAsBoolean."

The solution that go offers for such problems seems to be to deny that there is such a thing as libraries and to promise to deliver migration scripts whenever changes in the language or its libraries break existing programs.

That approach works fine now, but I am not sure it scales to a situation where go is truly successful; in particular, I think go, at some time, will have to allow for dynamic linking to support commercial libraries, if it wants to become truly successful.

On the other hand, the times of copy protection on software being normal are far behind us, and CLR has an ecosystem for commercial libraries, that decompile so well that CLR libraries do not differ much source code. Maybe, I am underestimating the willingness of commercial vendors to start shipping libraries in source form.


I don't get where people get the idea that Go is simple.

1) go has generic functions : http://golang.org/pkg/builtin/, they're just not accessible to you

2) go has generic types : slices, maps, and channels, all of which have slightly differing behaviour, including some highly counter intuitive and contradictory behaviour (example: slices are pass-by-reference, arrays are pass-by-value. In other words []int and [5]int behave entirely differently)

3) go has overloaded functions : http://golang.org/pkg/builtin/, they're just not accessible to you

4) go has exceptions (panic/recover), negating all the advantages of Go error checking, and providing zero fixes for the problems it introduces (finding the source line where an error happened and if/how multiple errors are related. Easy in Java/C++/..., hard in Go) (in C++ you have to be aware of whether any external code you use throws exceptions ... in Go you have to be aware if any external code you use panics. And if you say I haven't dealt with it, that means that you quite literally haven't dealt with it. Same as in C++. External libraries throwing exceptions is perfectly fine ... as long as they never actually throw an exception. Panicing standard library is fine ... as long as it doesn't panic ... If you're looking for correct code, it is of course not fine)

5) Go's "simple" threading and panics. Try crashing a Go program with shared data with a null-pointer derefence in the shared data. Someone please explain to me how the resulting output is simple.

6) golang's own compiler and standard library are not in fact idiomatic Go. This goes from small problems (like total lack of unit tests in quite a few places), to larger ones, like not using interfaces for logging.

7) interface{}. I just grepped a reasonable, thoroughly reviewed codebase I've written, of several tens of thousands lines of Go. Result: 2.7 uses of interface{} per 1000 lines of code.

Is this what people here call simple to think about code ? I would argue that you don't understand a codebase until you've read it. That's a certainty. So can we at least agree there's a point where verbosity no longer increases readability ? I hope you can see Go is far past that point.

Go's type system : riddled with exceptional behaviour. Literally it says "type system applies unless it's doing <-, cap, len, copy, new, append or delete calls", in which case we do something custom. The resulting behaviour, of course, is inconsistent with Go's type system (which is really the point of the code of course, unfortunately, there's surprises buried in there, and those calls are inconsistent with eachother as well).

All of these are implemented as exceptions in the compiler. I would warn you about this link : once it is seen, it cannot be unseen :

https://github.com/golang/go/blob/master/src/cmd/internal/gc...

The irritating part is that this sums up Go mailinglist behaviour quite well. Coding practices is for you, not for us, the Go authors. Generics ? Of course we need that, but you obviously don't as you're a bad programmer. And if you're asking for something else that must mean you suck. Overloading ? Ditto. Generic types ? Yes for us, not for you though !

Of course, all of these claims are thrown around and when you actually read the compiler code you wonder ... do these guys know about type systems ? Don't these guys know about the standard coloring algorithm ? Why aren't they using extremely-well-known algorithm X for this ?

In other words, it really looks like the compiler was (and is) written by someone who last opened a compiler book in the 70s. But reading the mailinglist, you'd think they were the central authority on compiler theory and you're stupid. This is in fact not the case.

Go works and feels like a late-90s language like Oberon. Clean at the surface, with lots of buried ugliness. Lots of weird custom datatypes, forms and parts of the standard library that are not implemented in the language itself. It's why I switched to C/C++. Go is simply the same as those old languages we moved away from. There is nothing in Go that cannot be achieved better and more flexible in C++, D and Rust.


> Clean at the surface, with lots of buried ugliness.

Maybe we will see a DSL that compiles into Go to fix this. Like we have for Javascript.


What would be the point? You may as well just target LLVM.


To benefit from the whole Golang ecosystem.


Fair enough.


If you want a more complex Java you can have Scala. Scala remains hostile to newcomers with an almost vertical learning curve. I don't think anyone can seriously argue that commercial programmers need a more complex language.


We have data to contradict this argument: Over 400'000 people have enrolled in an online Scala course with a success rate twice the industry average. "Almost vertical learning curve?" Please!


"Why should I ever wash my hands before a medical procedure? It just makes things more complex!" –– Surgeon, ~1700.


What a feckless comparison. Don't mistake something taking more time with something being more complex.


Comparing Scala's complexity to a life-saving procedure says more about your own self-importance than anything.


Strong type systems absolutely save lives.


I thought the "sum" example was pretty damning. Bug count scales with code length so writing tons of boilerplate code to work around language limitations means you're introducing bugs.


This, and boilerplate != simplicity. The D example in the article - now that is simplicity. Language designers would be well served to acknowledge this.


As a programmer who is writing Go for a living: the lack of generics is not actually that big of a problem in your day to day work.

Yes, if you want to generalize code to release a library to a much wider audience, you'll either end up writing quite a bit of duplicate code, using the code generation tools, or diving into the "reflect" package (which would let you write Sum in one method).

That said, I've never had to do either of these in writing about 10k lines of Go code.


I believe that the purpose of `go generate` is to lessen the risk of this type of thing by handling the repetitive code generation for you, but I do agree that it's a limitation. If done by `go generate`, it's an additional procedure that you have to learn; if done by hand, it's a bug risk.


Code generation is a giant spiked mallet for a nail which can easily be screwed with a small screwdriver. It has its uses, especially for generating schemas (like what Protobuf and Capnproto do), but for generating actual function implementations it's kind of absurd. `go generate` is a nice tool but it's not even remotely a solution to the no-generics problem.

Of course, the problem is that for the compiler implementation, those traits are completely reversed (adding generics at this point is like hitting the compiler with a mallet); and that happens to be Go's primary excuse for lack of generics/templating/macros/anything sane. I think dev ease-of-use matters a lot more than core dev ease-of-use though. Even if the compiler must grow in complexity and average compile time, the benefits to devs are worth it.


As someone who has repeatedly done code generation on the C++ side to avoid build-hell (text files with expansion into C++ files) -- I can't agree with this. Your trivialization of complex C++ build times is nonsensical and reeks of inexperience -- it ruins everyday -- it ruins cycle time -- it sucks joy and creativity out of development... long build times are atrocious which is why insane effort is being spent to try to lower them in C++.

Lots of C++ "features" on banned on major projects because of complexity and build time issues -- templates, exceptions, operator overloading...


No one has ever said that the compiler implementation would be too hard, and that's why there's no generics in go. People need to stop saying that, because it's bullshit.


From the Go FAQ (https://golang.org/doc/faq):

>Generics are convenient but they come at a cost in complexity in the type system and run-time. We haven't yet found a design that gives value proportionate to the complexity, although we continue to think about it.

Not hard as in "can't do it", but hard as in too hard to implement without adding the ever feared "complexity".

For the record, I like Go and its philosophy, but I think, above all other issues with the language, the lack of generics is a huge weakness and will eventually be looked back on as a mistake. Not just for not having them before 1.0, but also for waiting as long as they did for adding them, assuming they ever do add them.


The type system is a concern about the complexity for the users of Go, not for the compiler developers.


How exactly? Ignoring issues of backwards compatibility for the moment, I don't think Java's introduction of generics added a ton of complexity for developers, and I don't see why Go would either. In Java's case it seemed like the complexity was definitely on the compiler side, due to their type erasure approach.

I admit I'm not an expert in programming language design though, so I may be completely wrong about both Java and Go.


In my opinion, bug count scales within a code base with code length, but you can't really compare languages and say that the shorter one must contain fewer bugs. A more expressive language means you can cram more bugs in fewer lines.


Take a look at this: http://programmers.stackexchange.com/questions/185660/is-the...

Bug counts/kloc seem to be consistent across languages.


To add to this, implementing a bunch of boilerplate often means copy/pasting code. That means you risk introducing mistakes where one copy is wrong[1].

[1] http://www.viva64.com/en/b/0260/


You assume that Go's simplicity means you end up copying and pasting a lot of code. This is just not the case. In juju (https://github.com/juju/juju), a ~300kloc project, we have approximately one single type that we've copy & pasted where a generic implementation would have been better (it's a set). And honestly, implementing full set functionality was not even really necessary, someone just felt like doing it.


> PHP's package manager, for example, is not coupled to the core language

"Lacking a package manager isn't a language issue, this language that's nearly universally derided doesn't have one."

I see what you're saying, but that strikes me as a really bad example. That being said, it is strange to me that a modern language wouldn't have any reasonable story around package management. The argument I've most commonly heard is that applications should be very small bits of functionality, thus making a full-blown package manager unnecessary...sure, that seems reasonable...except dependency management is still (aside from the whole generics flame war) one of the most common complaints I've heard from Go devs.

To be honest, I don't understand all the vitriol. That's by far the most bizarre thing about Go to me.


A more appropriate example might be Python. It didn't ship with Pip by default until Python 3.4 (released in 2014).


It's bizarre to me that you started your post by dismissing an entire language based on popular opinion then end it by saying you don't understand the vitriol people have about a different language.


I personally don't understand the vitriolic posts on Go. I personally do understand the vitriolic comments about PHP, of which there are examples that are well-reasoned and go beyond "no generics!"[1]

[1] http://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design


It's 2015, the PHP ecosystem has progressed. Citing that blog post hurts your argument more than it helps.


The blog post is only three years old, and the problems it brings up are basic language problems. Has the language really changed that much in three years? And if so, wouldn't that leave all the existing projects out in the cold?

Serious question. I don't know the PHP community at all.


I do a fair amount of PHP (really, I run hhvm, but let's say modern php), and effectively nothing in that article affects me.

No one should be using mysql_* anything; no one should have been using it for years. It's deprecated. It throws warnings when run. PHP's love of forced backward compatibility causes some very interesting things to be remained.

Paamayim Nekudotayim isn't called that anymore and hasn't for a long time.

The author raises issues about things like @fopen(url) - no one I know, or myself, has ever done that. It makes absolutely no sense to.

PHP has autoloading/"modules"/dependency injection/a proper package manager/etc (article is from early 2012: composer was largely popularised from what I can see a bit after that).

The author complains about no stack traces - sure, there won't be any on a single file bad function name, but any time I've used a proper request library or anything best practice, I've gotten a proper stack trace with file names, lines, etc.

--

There are a lot of somewhat odd complaints or wildly strange, like "No Unicode support" and "PHP is naturally tied to Apache. Running it separately, or with any other webserver, requires just as much mucking around" which makes absolutely no sense to me. The author of this article even says that if you want to run two versions of PHP, you rebuild Apache? That's absurd.

> 'The “bunch of files” approach, besides making routing a huge pain in the ass, also means you have to carefully whitelist or blacklist what stuff is actually available, because your URL hierarchy is also your entire code tree'

None of this makes sense either: unless you are doing some seriously outdated horrors, you have effectively one /index.php router and a public asset folder for the webroot.


Thanks for typing all that out. It's great to hear that most of what that article complains about is not affecting current PHP projects. I know there's a ton of PHP out there, so it can't be that bad. :)


>So, we are left with the lack of generics and the lack of expressivity. I'm not deep enough in the weeds to be able to argue pro/con for generics intelligently right now, so I will concede that as a concern that has been raised by many.

"Generics" is not a feature, its a bugfix. It refers to the type checker not rejecting perfectly fine code. In practice, you can just read 'generics' as 'this product does not contain asbestos', or "the mandatory type checker does not reject perfectly fine code".

Type systems are supposed to help you catch bugs at an earlier stage than run-time execution. However, implementing a type system is very hard and academic in nature, and the type of people who are good at making production ready high performance compilers generally aren't very good at type checkers. This is why many languages initially launch with a broken type system (and often a broken garbage collector), but eventually patch things up.

The problem Go has, is that its main author makes such cringe-worthy statements about generics, that we now have a large group of enthusiastic people who, on the basis of authority of the author, exclaim equally confusing remarks. Everything got so defensive that this typical launch bug, normally fixed with a release or two, is now part of the culture and DNA of this particular ecosystem. The sad thing is, these are all talented people, that instead of impressing us with their awesome skills, are embarrassing themselves in front of anybody who ever read anything about category theory.

For example, here's a "generic" algorithm to fold the (+) operator in Javascript:

        function( xs ){ var result = 0; for( var i=0; i<xs.length; i++ ) result = result + xs[i]; return result }
No type checker, no fuzz. There is nothing complicated here. But if you translate this to Go, you would you would need to copy this code over and over again for every potential leaf type you want to use this algorithm with and change only the type signature. The complexity is actually the result of a mandatory type checker that is broken by design. The most simple solution to support 'generics' would be to make the type signature optional.


"Expressiveness" seems like a subjective measurement to me. Also, from what I've gathered, Go attempts to be more convention oriented and is centered around doing things the "one true way". The Go formatter is an example of this. Perhaps this is just a result of that philosophy.


His Go is a little disingenuous. This (https://gist.github.com/EricLagerg/105431503d32f18d239b) is almost as short as his D code, and functions the same.


It's almost as if, like most people who write articles complaining about Go's lack of "expressiveness" and generics, he took a cursory look at the language, wrote some naive examples that supported his point, and squeezed out a blog post.


From the very first line of the article:

>Over the course of the past few months I’ve been using Go to implement a proof of concept program in my spare time.

It's almost as if, like most people who post on Hacker News, you took a cursory look at the title, wrote some inane comment that supported your point, and squeezed out a reply.


If the code was equivalent you might have a point. Unfortunately it is not (tokenizing/line scanning vs. copying). One has a shortcut in Go, the other does not.

Would you say that Go is not excessively verbose in non-trivial use cases ? I recently had to sort a struct list in a program. Added lines of code for sorting a single list once : 30. What the ...


There's an equivalent method which can be called to read individual lines: ReadLine. There's also join and split methods to more naturally model what the D code is actually doing.

    fileBytes, _ := ioutil.ReadAll(<file>)
    text := string(bytes.Join(bytes.Split(fileBytes, '\n')))
    fmt.Println(text)
> Added lines of code for sorting a single list once : 30. What the ...

Sorting requires implementing three methods, at least one of which is usually given to you for free (Len). A simple case will usually cost about 9 lines of neatly formatted code, 3 if you're not so neat (which the Go sort library does itself).

    func (sl structSlice) Len() int { return len(sl) }
    func (sl structSlice) Less(i, j int) bool { return sl[i].MyKey < sl[j].MyKey }
    func (sl structSlice) Swap(i, j int) { return sl[i], sl[j] = sl[j], sl[i] }
Ultimately, even this isn't a case of verbosity so much as it is not abstracting away the basic sorting functions.


Mind if I ask why these functions are laid out entirely differently from almost every other function in the go standard library ?

Yes, I know the Go authors do it too. And of course we know the answer : they're ashamed of just how bad it is, and mask it by making exceptions in the style guide for this specific case. You also left out the type declaration and the actual sorting call. Here's the full code :

  type myStructSlice []struct

  func (ms myStructSlice) Len() int {
    return len(ms)
  }

  func (sl myStructSlice) Less(i, j int) bool { 
    return sl[i].MyKey < sl[j].MyKey
  }

  func (sl myStructSlice) Swap(i, j int) {
    return sl[i], sl[j] = sl[j], sl[i]
  }

  ...
  sort.Sort(myStructSlice(sl))
And for this trivial example we're at 15 lines of (properly indented) code. You also have polluted the name space with one-use names for types. Is it really so hard to believe that this easily explodes to 30 lines of code for real life examples ? The Less function is not going to be nearly as simple in real life struct examples (natural sort + field precedence).

Compare to the same code in Java:

  Collections.sort(theList, (SomeType s1, SomeType p2) -> p1.field.compareTo(p2.field));
D:

  sort!("a.field < b.field")(theList)
C++:

  thelist.sort([](const someType& a, const someType& b) { return a.field < b.field; });
So Golang is 5x more verbose than Java in this instance. This a particularly bad example, but that Golang is actually more verbose than Java is not an exception in my experience.


I'm curious about the specifics of your problem. I couldn't imagine sorting a list of structs (by one of the fields I presume) would be too terribly different in Go than in other langauges. Heres an example of insertion sort on a basic type: https://play.golang.org/p/SPoiNRVl2B

You can use the standard library sort methods by making your type implement the sort interface. Heres an example taken from the example in the sort docs: https://play.golang.org/p/oeRIhHi1Ei


I see you've grabbed some examples to show the simplicity. Having to write the insertion sort algorithm to sort your basic types is not simple.

Having to tell the standard library how to swap data in a slice and to obtain a slices length is simple... why doesn't the standard library know how to work with basic types? This way I don't end up writing by accident:

    func (a ByAge) Swap(i, j int)      { a[i], a[j] = a[j], a[j] }


This post contains a comparison : https://news.ycombinator.com/item?id=9267578


For anything larger than a trivial program, go is no more verbose than python. For small programs, there's too much variance to really say what language is less verbose, because you can always hit cases where one language has something in the stdlib and one doesn't.

The only place go is more verbose is sorting, and lack of map, filter, and list comprehension. Most of the latter just means you need a 3 line loop where you have one line in python. But that should not be a great effect on any reasonable size program.


You can say that, but it doesn't make it so. Go here: http://rosettacode.org/wiki/Category:Go for many examples of more succinct Python with better commenting than the Go code, for example: http://rosettacode.org/wiki/Align_columns


forgive the copy-paste response from elsewhere on thus thread.

an example from https://www.spacemonkey.com/blog/posts/go-space-monkey -

we decided to transliterate our 90k lines of Python directly to Go, line by line

The 90K number includes our tests, but without tests, the Python codebase is 36,784 lines of code. Those same lines of code became 41,717 lines of Go code. So, not that much of an increase. When you consider that's just 4,933 more lines, it's not crazy to assume most of those are closing braces.

I'd say closing braces and trivial expansions of list comprehensions into 3-5 line loops.

There's a real life example of direct transliteration, not just a rewrite. That's 13.4% more lines. I think that's pretty close.


Use io.Copy to copy from the file to os.Stdout. no need to read the whole thing into memory.


Hadn't even thought of that! Nice catch. It's even shorter: https://gist.github.com/EricLagerg/c3ccf96f6ed90d8b4557


Stop it please, or PHP will win this race easily. With just 1 line of code. And it doesn't mean anything.


a few more lines if you just let os.Stdout be the default: http://play.golang.org/p/FDbpCxCskT


If I wasn't already a Go programmer, reading this would make me more interested in Go than not.

Simplicity is deceivingly challenging, and quite different from simple-mindedness, which seems to be what the author is accusing Go of being.

Keeping code simple, elegant, and consistent is IMHO one of the most valuable principles a team can adhere to. Simple is not necessarily shorter, as short can often be subtle and sneaky rather than simple. Complex power tools can be fun to the inquiring mind, but the ultimate consumers of your product almost never appreciate how you built it, but almost always appreciate the final outcome.

A wise man once 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?” - Brian Kernighan


There's also the argument that your chance of writing buggy code increases with each additional line you write.

Highly readable code helps reduce bugs. But if that code is also so simplistic that it necessitates a lot of verbosity, you definitely increase the chance of introducing some stupid bug. Thankfully, as a compiled language, Go can find a decent portion of the silly stupid bugs where in a more expressive language like Python they might go hidden for longer, but it's still a big tradeoff you're making.


This topic we're debating is very well studied. There's lots of good research out there that attempts to correlate how predictive certain attributes of code are to future bugs. Here's one paper in particular that is a very good read and widely cited: http://research.microsoft.com/pubs/70232/tr-2005-149.pdf

As you would imagine, doing this is quite hard, and quite inconsistent from project to project. Lines of code may bear some correlation to future bugs in code for some projects and languages, but it's far from clear cut that more lines of code == more bugs or more complexity.


Thanks, that clears up some misconceptions I had.


Go is not significantly more verbose than python except in a couple trivial cases (list comprehensions become a 3 line loop). Across even a moderately sized program, this will most likely not even amount for a statistically significant difference.


As a professional Python developer and amateur Go dabbler, I very vehemently disagree.

Without access to list/dict/set comprehensions, or libraries like itertools, or a lightweight lambda syntax or something like Ruby's blocks, transformations on collections will always be considerably more tedious and verbose.

Various functions have "bring-your-own-buffer" calling conventions, which will often double or triple the number of lines required for those function calls.

No tuple unpacking, no ability for functional programming (Python's isn't that amazing but with functools and some of the builtins, you can get pretty far), no operators for string formatting or anything but absolute barebones operations.

Combine that with what Go lacks even compared to languages like Java (no inheritance [which is usually an anti-pattern but does decrease verbosity when used properly], no generics, no deep type/class reflection) and it's hard to say that Go isn't a verbose language.

No statically typed language is going to be as terse or expressive as Python can be.


Can you explain how tuples are significantly different from multiple returns in real programs? You can do

    func foo() (string, int, bool) {
        return "one", 2, true
    }

    func bar(s string, i int, b bool) {}

    bar(foo())


When dealing with function calls, they're roughly the same.

But how about things like:

    g = some_input() # A list with 3 elements
    a, b, c = g
Or in Python 3+:

    lst = [1, 2, 3, 4]
    head, *tail = lst # 1, [2, 3, 4]
    *init, last = lst # [1, 2, 3], 4
    a, *b, c = lst # 1, [2, 3], 4


You can do that in go with just a few more characters:

    g := some_input() // A list with 3 elements
    a, b, c := g[0], g[1], g[2]
I'll grant you, having to specify the indices is slightly more verbose, but it's also a lot more clear... because what happens when g has more or less than 3 elements? In Go it's clear, more is ok, less will get you a panic.

    lst := []int{1, 2, 3, 4}
    head, tail := lst[0], lst[1:]
    init, last := lst[:len(lst)-1], lst[len(lst)-1]
    a, b, c := lst[0], lst[1:3], lst[4]
Again, I don't think the magic unpacking is really helping that much... doing it the same way you'd do anything else in Go is slightly more verbose, but it's also not some new syntax you have to figure out either.


Go does seem more verbose than Python. Rosetta code tries for idiomatic solutions to common tasks, here's one such completed in both Python and Go: http://rosettacode.org/wiki/Bitcoin/address_validation

You would need to know the nature of the tasks to pick out those that need longer answers but all the Go tasks are here: http://rosettacode.org/wiki/Category:Go - most of which should have Python solutions.


Most of those programs are trivially small, and often consist mostly of transformative loops. Yes, go will be worse than python for that. But in a real project, the difference will be minimal, a few percent, which will be lost in the noise of the specific implementations, tests, etc.


Even so, Go seems less succinct in comparison on several tasks. Such differences will most likely be magnified in larger programs.


an example from https://www.spacemonkey.com/blog/posts/go-space-monkey -

we decided to transliterate our 90k lines of Python directly to Go, line by line

The 90K number includes our tests, but without tests, the Python codebase is 36,784 lines of code. Those same lines of code became 41,717 lines of Go code. So, not that much of an increase. When you consider that's just 4,933 more lines, it's not crazy to assume most of those are closing braces.

I'd say closing braces and trivial expansions of list comprehensions into 3-5 line loops.


That's definitely interesting, but I'd have to see both codebases to make any real determination. It's plausible their Python code was extremely imperative, overly complex, and/or not idiomatic.


How will you keep your code simple with all those manual error checks?

How will you keep your code simple if your procedural bent causes you to create dependencies/coupling at will?

How will you keep your code simple if your variables are mutable?

Recipe for complexity and bug hell


manual error checks are no more complicated than any other code that branches.

Implicitly fulfilled interfaces make it trivial to decouple code, because I can pass a type to a function in a different package, and that package doesn't even need to know that type exists.

You're obviously pushing the pure functional route, which is nice, but 95% of code in production is procedural. There's a reason for that. I could speculate why, but I don't need to.


> I could speculate why, but I don't need to.

I will. There's a pattern out there and it goes something like this:

1) Most programming classes still teach procedural

2) New, typical real-world programmer looks at procedural and functional, finds functional more alien and difficult (because it's far more dissimilar to the popular languages s/he played around with in their youth), settles on procedural

3) 10 years later, after spinning a significant percent of wasted cycles on piles of procedural spaghetti code and spending hours debugging classes of bugs that ended up coming down to a lack of managing complexity, various interdependencies, no recognized schedule to pay down difficult-to-quantify technical debt, and mutability... programmer looks at functional languages again and finds them not lacking

4) programmer takes on a side project in a functional language, thinks it's awesome, mind is bent in pleasant shapes, considers quitting day job or "the big rewrite", realizes the latter is not feasible (see: Joel Spolsky), gets frustrated with the status quo

5) programmer posts to future incarnation of Hacker News probably sounding like an ivory-tower prophet, preaches about macros and homoiconicity and immutability and the dynamics of programming teams, ends up angel-investing and founding an incubator instead of fighting the hopeless procedural-vs.-functional battle

6) Repeat.

;)

EDIT: BIAS: I think Elixir (http://elixir-lang.org/) finally has a real shot at breaking this cycle. It's the first functional language I've used that "felt" accessible enough to most folks (Ruby-ish syntax) AND has all the typical functional niceties AND has real macros without being jarringly off-puttingly homoiconic AND has an extreme focus on concurrency (perfect for the future Web... it can fire off a million PID's in a second) AND it embraces failure (good for anything that touches "the real world") AND uses the Actor model (a candidate for "most scalable language design pattern"). OO is, IMHO, dead man walking right now. It's too easy to pass mutable state around everywhere, it complexifies code too much by allowing any code anywhere with an instance of a class to call methods on it (therefore coupling the class implementation to the entire codebase), it doesn't realize the cost of inheritance, it is difficult to parallelize, etc. etc.


IMO, the author's "copy data to stdout" example betrays a lack of language understanding. Here's a more idiomatic version: http://play.golang.org/p/ZWatRuj3Q_


Plus given Go's focus on external dependencies, a common task like reading commandline parameters can (and should) be done with a library that solved that problem for you already - don't reinvent the wheel, and such.


Thanks! I'm just learning go (started last night), but I couldn't believe there wasn't a better way. It felt like a straw man example.


Kind of code golfing it: http://play.golang.org/p/s53zQrE0ei


That's definitely shorter, but I would hope that isn't considered the most shining example of non-obfuscated code. :)

(For code golf, though, it's just fine.)


It's actually very simple code, if you're at all familiar with go. That's probably what I would have written just by sitting down at the computer to do so for work.


Yep, that's what I would have written.


What happens when copy fails? Don't worry I already know.


I tried Go. It didn't really impress me, to be honest. I get that they're trying to build a nice, coherent language and, but they do that by simply avoiding doing all the things that are hard. I don't see any compelling reasons to choose Go over, for example, Java.


Go uses a lot less memory than java in general (generally due to having real value types and usable pointers). Go has clean multi-threaded code baked into the language. Go requires no external runtime, so you never have to worry about what version of the runtime is on a machine. The implicit interfaces make it easier to keep separation of concerns, and prevent your code from turning into spaghetti.


Languages should be criticized by those that have spent sufficient time in them to be able to really see their shortcomings. Having a little bit of experience in a language is not an automatic qualification for writing blog posts about what's amiss, for that you need more than just surface familiarity.


Seems to me the replies made to the "fake" problems are very telling about what is a problem.

For example, Go is praised for being a simple language that everyone can learn quickly, yet when someone shows what they picked up "that isn't how you do it."

I get that it is one thing to get something done, and another to learn an idiomatic approach. But when the sort package is hailed as the example for both why Go doesn't need generics and Go does need generics, I think it is safe to say that even the language creators don't know how to use Go.


This topic periodically flares and provokes so much emotion it is almost unseemly. The two camps are so diametrically opposed that I think other factors are at play here. Programmers who cannot or don't want to understand some of more advanced/esoteric features of expressive languages like C++ or Rust insist on the virtue of simple languages because it guarantees job opportunities for them. Their slogan is group power; quantity has a quality of its own. Interestingly, communists have similar slogans, who are mainly intellectuals self assured to govern/manage the mass and be handsomely compensated. I never realized the common interest between the top and middle, or how corporate the communism is. The other camp is equally vehement because their livelihood is at stake. If all their time and effort will not make them better/more productive programmers, the upper limit to their compensation is drastically lowered, not to speak of damage to their self-image and statue among peers. They are the liberal individualists who insist that there is no limit to what one individual can achieve or make. There is no need to debate this further. Everybody WILL not change their mind. Go will go far, just look at Java.


>Go will go far, just look at Java.

How did you reach this conclusion? I also don't understand the odd comparison to communism. Who's the communist in this scenario?


You shouldn't be offended that Rob Pike thinks he's a much more advanced programmer than your average programmer.

He is.

Whatever his motivations, he created a very productive language. As it turns out, even advanced programmers benefit from having a simple language when it comes to getting practical work done.


> As it turns out, even advanced programmers benefit from having a simple language when it comes to getting practical work done.

I'm making this point quite tongue in cheek however...

By that logic why do programmers hate Visual Basic so much? At least VB6 was very much a "simple language" relative to other things around at the time (less so with VB.net/C#).

A lot of the arguments people are making here (less is more essentially, and clear syntax wins over complexity even at the cost of efficiency) then VB6 should be something we're modelling modern languages off of, but it is not...

PS - Remember I am not being literal here. VB6 had some issues both then and now. I am not really comparing Go to VB6, I am comparing the arguments made in support of Go to arguments that could be made in support of VB6.


There's a difference between being "simple" and "brain-dead". C, for example, is very simple, simpler than Go, even, but you can't compare it to VB6 either. Your comparison is disingenuous at best.

Simplicity is a good thing. It's a virtue. It's part of what made UNIX a success. It's not something to shun or avoid.

Adding features adds complexity and complexity is not free.

Notice how the industry still has't jumped ship whole-sale to Haskell?


Where did you get the idea that Haskell is a complex language? The core language is just 6 or 7 different expressions, everything else is syntactic sugar - it is in fact much simpler than C.

One of the reasons it's not widely adopted is that the features that programmers take for granted, like built-in syntax for mutable variables, are missing, i.e. it's too simple.


> Simplicity is a good thing. It's a virtue. It's part of what made UNIX a success. It's not something to shun or avoid.

It seriously depends what you're doing. As someone put Haskell recently "all the possibilities of advanced algebra combined with all the clarity of advanced algebra".

Simplicity is only a real success if it doesn't throw out the baby with the bathwater.

> Adding features adds complexity and complexity is not free.

And taking away features is free ? Why aren't you programming in LISP then ?


Until the Golang community can move on from "it must be right because Rob Pike" then things won't improve.


This, and it applies to the creators of other contemporary languages as well.


Not always. I'm sure he is more advanced programmer than ME personally, but I'm also sure there are programmers who... know some aspects better than Rob Pike. "You shall not make for yourselves an idol".


I have to agree about the generics, and agree a bit about error handling too.

Also, I wasn't impressed by the speed. I wrote a program in Ruby, Go and Elixir and expected Go to win, but is was 1x, 5x, 20x respectively, and I could never figure out why there was such a discrepancy.

Even so, if they added generics and improved performance, I would be very inclined to use it. It is surprisingly comfortable to code in.


I would wager it was human error.


1 program in Ruby to measure the speed of whole language is not enough. https://xkcd.com/605/


Just because you're intelligent doesn't mean you should spend all that intelligence trying to figure out other people's messes. Simplicity and explicitness are paramount when collaborating.


> Simplicity and explicitness are paramount when collaborating.

So is succinctness (most people can't speed read code). The examples he gives make Go look anything but succinct.


That's because he's not a very good Go programmer, which is not surprising since he's clearly far too intelligent to use Go.


At a certain point succinctness does more harm than good. Go captures nearly all of the sense of python's early values (https://www.python.org/dev/peps/pep-0020/), without requiring all that much more in the way of boilerplate. So you have to if-check an error value here and there, no big deal. You should be handling errors anyway.

If Haskell's succinctness level is 1, then I would rate python's at 2, go's at 4, and java's at 20.


Nonsense. On moderate-large codebases Go is not 5x more expressive/succinct than Java. Maybe 1.5x if you are lucky.


Please point me to the large codebase your ported from Java to Go -- if you don't have actual data, just two people making up idiotic numbers let me get involved.

Go is 185175% better than Java and 8165% better than Haskell -- or making up numbers just makes us all look like goddamn idiots.


Presumably, that rating scales has higher numbers meaning less succinctness? Because otherwise the order of the numbers (much less there relative values) doesn't make any sense, even though that's kind of the opposite of what you'd expect of a "succinctness level".


It's a linear scaling factor for how many lines of code I tend to take to express the same medium-sized program across these languages.


I'd be really curious to know what this guy thinks of Rust.


I'm a bit curious too - he seems to hate the idea a programming language thinks the developer is an idiot.

Rust, for how perfect it really is, treats developers as humans. You must be smart if you can use it - but it doesn't trust you to have any idea what's really going on when a program gets large. This is a good thing IMO.

Honestly, anyone who thinks Rust is trash is just wrong. I can get the argument that there just will never be enough people who can use Rust but having learned Go and then Rust - I have to say Rust is the most well designed programming language ever. I can't believe what's in it and it's far and away the most advanced programming language ever. It's hard to compile but when it does you have a good chance it's right.


I heartily disagree that you have to be smart to use Rust. I'm a ho-hum CRUD web programmer with enterprise credentials and versed in dynamic languages, and I was able to pick up Rust quite easily as my first systems programming language ever. The hardest part isn't even learning the language, the hardest part is figuring out the essential differences that separate the systems programming domains and the high-level dynamic domains.


That's a great endorsement. I was going to do my next experimental app in Go, but this really makes me want to use Rust.


Haven't seen anyone trash Rust yet.

However, one thing I'll say is that it's a big problem for people like me if a language requires you to be smart to use it. It's really, really hard to hire only smart/experienced developers.

What I need, and what I hope Rust can be, is a language that is opinionated (in the right ways) and won't compile if an inexperienced dev tries to do something dumb.

In Rust, the fact that variables are immutable by default (but can be explicitly declared mutable) is the kind of thing I'm talking about.


Woah, I can't tell if that was a brilliant troll or you honestly believe these things -- either way +1 hilarious.


What do you dislike about Rust?


I can confidently predict the author will decide that Rust has some nice attributes, but pales in comparison to D.


The author is basically saying that you'll be better off banging your head against a wall trying to figure out a more complicated language than to pick up golang quickly an start building something.

News flash! Not everyone is a brilliant developer. And that's okay!

News flash! Not ever developer has extensive programming experience and can pick up new concepts quickly. And that's okay!

Go is too simple for you? Drink a big glass of shut the hell up and go write a program in brainfuck or something.

Intelligent programmers aren't dependent on the language they use to become better developers. There is no disservice in being able to build something quickly.


Most of the negative reactions to Go aren't really to do with the language itself. They are more general feelings of frustration with our industry.

Part of the reason Go is so popular is because we place such a low value on education, training, and long term productivity, and a high value on getting things done quickly. In general our industry is focused on the short term, is fad driven, and is more than willing to turn on our own.

Go is a language created to win in this kind of environment. And because people hate the environment... they focus their animosity on the tools that are successful in our environment.


> more than willing to turn on our own

This "it's so simple!" nonsense is an exemplification of that.

By reducing everyone (even good programmers) to the lowest common denominator, talent is far more commoditized and pluggable. You can throw away Programmer A because Programmer B is "as good as it can get for


Syntactically simple language actually tend to be harder for new/less-skilled programmers, as they require the programmer to figure out how to do more things that more complex languages handle as simple expressions.


Man, I've lately seen so many posts criticizing Go. They must have done something pretty well with it. It's sad that I see more posts criticizing Go than posts talking positively about it.


To be fair, it feels like survivor's bias. People are more vocal about criticism. When you agree, well, what is there to say? Nothing, you just use it, instead.

In that light, the actual usage statistics are probably a better metric of the language's success than the +/- ratio on hacker news.


I hear you and I have been a fan of Go for awhile. But, it hasn't really done anything new, especially in regards to programming language theory over the last 30 odd years. It feels like another "developers, developers, developers" play and when you see ultra modern languages like Rust, you sort of get the feeling Go is half baked and without purpose.


I completely understand most people's complains about Go. I think most programmers that have used Go understand that it doesn't offer anything new, besides very easy concurrency and a few other things. Still, I think it has a place, and it seems a few large companies are using it with success, and, at the end of the day, if people find it practical, then way to go.


Its purpose is to help ship products. It isn't sexy, it isn't fancy, it isn't interesting, it isn't groundbreaking. If it doesn't help you ship products, don't use it.

Go will continue to grow at the insane pace it has because it hit a sweet spot and cared about standard library and documentation. You will continue to see high profile successes because it doesn't try to be fancy, it tries to get out of your way so you can do that job of actually transforming data. So you can be a programmer and it can be a language, not a hobby.

I am a language geek, I write DSLs on a regular basis for fun -- and I love any language that is DSL friendly -- but for actually getting my day to day "must ship", "must work", "must scale" work done -- Go to the rescue. Great docs, easy to train people on, ultra-simple spec, multiple compilers, easy enough to integrate C code, and a really great vibrant community.


Sort of like my feeling about politics - if the far left and far right hate you, you're probably on the right track.


Knowing its history, if you're really hated by the far left, you're probably part of it too :D


Author's examples are really exaggerated, but there is definitely some truth. Bufio and flag packages are not that good. And "idiomatic" Go is full of poor abstractions. More often than not it feels like some things are written by OO programmers and are unnecessary hard. Like people don't know there are function literals in Go and they are free to abstract away state changes, instead of creating objects and spreading them all over the place.


I'd like to hear more details of your issues here. What's not good about the bufio package? What do you mean by "poor abstractions"? What's wrong with creating a single purpose local type rather than using a function literal?


The flag package might not be perfect but the author wasn't even using it properly so it's hard to judge it based off of this example.


It's pretty funny how this post is on the front page adjacent to a post profiling one of the Go authors that opens with mentioning his masters/PhD from Harvard/MIT.


And for more contrast, author of critique article can only brag "past few months I’ve been using Go to implement a proof of concept program in my spare time". I really hate articles from noobs who thinks they have a right to criticize something without being proficient in it. And not only about Go. At least HN comments are interesting.


It's the difference between simple and easy. Go is very easy for existing developers to learn because it is basically a cut down subset of what they already know.

Which makes Go a great option.... if you use it properly. It means you don't need to worry about a programmers language history when you hire even if you need them to be productive right away.

It means you can hire the smartest developers, regardless of whether they have been doing embedded work, or spas in js, or large enterprise application dev in java, etc.


In other words: reduce everyone to the lowest common denominator.


But I'm a special snowflake! This won't do!


Of course a lot of good developers won't like it because it is a change that makes them worse off.

If you are a good Haskell/D/Lisp developer then switching to Go will probably halve your productivity over the long term.

There are a lot of benefits to using Go but lets not pretend there aren't any downsides.


The author is trolling or has some weird superiority complex, don't rise to it.


Ahhhh, he's a D programmer. That explains most of it. D programmers are only slightly behind C++ programmers in their universal hatred for Go. Rubyists can learn to love Go, Pythonistas can learn to love Go, NodeJs people even, but D programmers, man, it's like just the mere existence of Go is an assault on their very souls.

If D and C++ programmers hate Go, it's doing something right.


I hate to have to refer to something so stereotypically "Hacker News" (as it was witten by Paul Graham), but this seems like a great example of the Blub Paradox playing out: C++ and D offer some really key features that all those other languages don't, so the reason you are more willing to tolerate Go is because you don't find yourself thinking "how could someone seriously have designed a language without these key features". Now, you might try to argue that it is somehow C++/D that are missing out, but FWIW: I have been programming in Python for seven years, and have probably at this point written as much if not more Python than I ever did C++, and the only reason I do it is because there are some "trivial" (and yet key) tooling and library advantages... the language itself honestly just feels "dumb".


Awful ad-hominem.

Who cares if all D programmers hate Go? Maybe for good reason. Or maybe they're just being erratic. Who knows? Not the readers of this comment, because it doesn't contain any constructive argument.


No, it just means there is nothing in Go that would cause a D or C++ programmer to switch to it. The feature set of D and C++ is a superset of Go feature set.

This "they laughed at Einstein, so I must be right too" mentality is pure fanboyism.


As a (mostly ex) C++ oriented engineer, there is a hell of a lot that would make me prefer Go to C++ for a lot of tasks. A language is not about its features, it's about how it facilitates what you need to get done.


Go was built for C++ programmers, so your point is not valid.


"So Rob Pike is basically saying that the developers at Google aren’t very good so they’ve developed a dumbed down language so they can get things done."

I read the Pike quote very differently. When Pike says: "The key point here is our programmers are Googlers, they’re not researchers." I thought he meant programmers today use google to assist them in programming.


"Googler" means "someone who works at Google". He's saying the language is designed for people who are in large, transient teams building large-scale production systems, not people who want to explore advanced programming language concepts.


"The key point here is our programmers are Googlers, they’re not researchers."

I took that to mean "Our programmers are building products, not thinking about building programming languages"


It may go even further than that. Google has to produce multi-million line programs and maintain them for a decade or two. That's not a problem that most programming language researchers face. If researchers think about that, they usually do so in the abstract, rather than from a position of personal experience.

One place this shows up: Build times. Turning a 45-minute build into a 10-second build, times 30 programmers, times several builds a day for 10 years... that adds up to a really big difference.


It always astounds me when (inexperienced) developers play down build times as if they simply aren't that big an issue. They are hellish, creativity crushing monsters.

Also, static binaries -- so the DevOps guys don't hit you with sticks.

I still remember the first go program README.md I shipped. "Umm, get foo to box, ./foo in a way so that it runs persistently and at startup."

Easy way to get your apps to the front of the line in the deploy queue -- all the time.


The odds of a single individual being an "intelligent" programmer is much lower than the odds of a team of all present and future programmers for a project being intelligent.


Uh..yes, I meant much higher, not much lower :(


> In my opinion Go has been designed by people who have been using C all their lives and don’t want to try anything new. [..] > Another grating issue is that because Go is procedural (just like C ‘shock horror’) you start writing code in a procedural way which feels archaic and old hat.

Ok, Go is not object oriented, but it has a few features that make it possible to do some stuff that are very cumbersome if not impossible in C.

- first class functions and closures

- you can safely take an address of a local variable and use it after the function's scope exits (the compiler takes care about promoting variables to the heap)

- interfaces allow you write modular code: e.g. you can create your own Reader and Writer interfaces and the standard library can operate on it. People are focusing much about generics/templates but it's not that Go completely lack any way of creating modular and reusable code.

- pointer/value distinction but having safe pointers by disallowing pointer arithmetics and instead providing runtime bound checking (slices)

- garbage collection (this topic actually opens a can of worms of it's own, regarding the placement of the language on the spectrum)

If you take C and add those features (instead of starting from another language and removing the features, see Blub Paradox) it's more apparent that it adds some value.

One could argue that the comparison with C misses the point, given that one of the main reasons C is still alive and kicking is because of it's ubiquity and the fact that its calling convention is the de facto standard of modern operating systems, all of which is defeated by introducing a new language (with a non-C calling convention!) that doesn't even aim at the level of control (especially over memory allocation) that C gives.

However that's where Go's cultural roots are. Well, except for the concurrency parts and other bits that were inspired from other languages, but when talking about sorting or reducing arrays, that part stems from the C culture.

I have the feeling that one of the reasons Go is so successful, is that there are many people who would actually like C but recognise what a giant time sink can be to become proficient and write good quality software in it.


Good article, but the "would of" ruined it for me. Did you not proof read it even once to let such a horror through?


The most important point the author is missing is code readability - being forced to write opinionated code is one thing. Having a chance to read somebody elses code (or your own code 3 months later) and avoiding eternal debate on code formatting is priceless. Programming is not about being able to outsmart others, in particular not in a large-scale project.


...every large scale project I've worked on has had a set coding standard and supplied auto-format tools either as IDE plugins or as part of the checkin procedure to make it easy to adhere to the standard.


On my phone, but his examples just show that he's new to Go. He's using like 5x add much code as he needs to.


Possible explanations:

* the author (and everyone else who complains about Go) simply hasn't made any appreciable investment in learning Go

* the language itself doesn't naturally lend itself to most programmers easily finding the most succinct/powerful expressions available, instead providing a grain that favors clarity over other values

* the documentation and tutorials available don't teach programmers how to write succinct and expressive Go


> the language itself doesn't naturally lend itself to most programmers easily finding the most succinct/powerful expressions available, instead providing a grain that favors clarity over other values

I think that can be said of pretty much all programming languages.


It's not about the language or expressions. He just isn't very familiar with the standard library. His code was not unclear or overly verbose... it just didn't use libraries to replace most of his code.

So, yes, it was exceedingly obvious that the author had not made any appreciable investment in learning the language.


T


hey man, don't say anything negative about the G. It goes on your permanent record!

/s

edit: apparently sarcasm does not go unpunished. ah well.


I've had a hard time making up my mind with generics vs interface in go, but i think now that any compelling argument should be made using real world problem from a real world project.

Nobody is coding the sum of a list of integers for all the various integer types. People more likely use custom types anyway, and only have to create operations on those custom types ( except maybe for go language designers themselves). So there's always a suspicion that somehow there should be a way to code something clean using interfaces only.


People more likely use custom types anyway, and only have to create operations on those custom types ( except maybe for go language designers themselves).

People developing whole applications use custom types. People developing libraries meant for general use seem to prefer not limiting their stuff to working with their language's comparatively few built-in types (at least, that's how things seem to go in languages with generics -- Guava's BloomFilter should work with any type, even if that type was created well after Guava was written).


You're right, librairies developers are probably the one that should complain, and yet go is also famous for having a really extremely convenient standard lib (such as http), which means it should be possible. They are often built on top of a tiny set of orthogonal interfaces rather than generic types (see sort.Interface as an example of something quite low-level).

So that's why i'd like to see real-world problems that illustrate some concrete examples.


It's no surprise that an HTTP library doesn't have issues with the lack of generics -- it shuffles text around. Reusable data structures are the obvious case for genericity, and I don't see many in the docs. Once you have a higher-order language (whether via objects or first-class functions), some control structures become data you might want to use generically. Looking over Java's stuff also shows some interfaces one might want to parameterize over a type (e.g., Comparable -- what can you compare this to?, Future -- what type will we get from it?). Haskell also offers a lot of examples of really exercising polymorphism (like lenses, but those maybe aren't appropriate for Go).




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: