Hacker News new | past | comments | ask | show | jobs | submit login
I Love Go; I Hate Go (dtrace.org)
279 points by tominous on Aug 2, 2016 | hide | past | web | favorite | 316 comments



The most frustrating thing for me, by far, is that Go won't let you import unused packages. When you are commenting stuff out to debug a program, or adding debug statements and removing them later, it constantly requires you to go back to the top of the file and comment out the unused libraries, only to uncomment them later when you've solved your problem.

The fact that there is not a compiler flag to disable this behavior, and that the core team is opposed to adding such a flag (https://golang.org/doc/faq#unused_variables_and_imports), is simply ridiculous and makes programming in Go a real pain in the ass.

I can't think of any other language that does this.


I used to feel this way. It was my top complaint about the language. But it, and, more importantly, the use requirement for variables, has saved me from bugs repeatedly; I more and more notice the bugs it's protecting me from as I keep coding in the language. I am rapidly coming to the conclusion that this was very, very much the right call.

I get the sense that most Go programmers use "goimports" to work around the annoyance. I have my Emacs configured to use it automatically. I never even think about imports anymore.


> I get the sense that most Go programmers use "goimports" to work around the annoyance. I have my Emacs configured to use it automatically. I never even think about imports anymore.

You haven't yet encountered clashing names?

https://golang.org/pkg/crypto/rand/

https://golang.org/pkg/math/rand/

Or?

https://golang.org/pkg/text/template/

https://golang.org/pkg/html/template/

goimports is wonderful, but some care is needed to make sure it has added the one you intended to add.

NB: Only an issue when new imports are added, not an issue if the correct package is already referenced.


The minor inconvenience of occasionally having to fix a path when goimports picked the wrong one is far outweighed by the massive convenience it brings me every day.


I have been using it since I switched to VSCode about 4 months ago and thus far, knock on wood, it's always found the one I intend. I however do think it considers what public members/methods you are calling.


"considers"?

It just finds a match and goes with it, and that does cause issue. Both of the above have potential security implications.

Filippo suggests preferring crypto/rand over math/rand as a softfix in this twitter conversation: https://twitter.com/filosottile/status/752210041709719552

I'd probably prefer html/template over text/template as it's better to have someone ask why their output was HTML escaped than to have someone render HTML in plain text without explicitly wanting to do so.


"But it […] has saved me from bugs repeatedly; I more and more notice the bugs it's protecting me from as I keep coding in the language […] most Go programmers use "goimports" to work around the annoyance. I have my Emacs configured to use it automatically. I never even think about imports anymore."

If you don't think of imports anymore, how can they save you from bugs?

(I do (somewhat) understand the "more importantly, the use requirement for variables" part, but also there, I think a warning should be sufficient. Your editor could/should color-code dead code, anyways, making it stand out before you compile)


The use requirement for variables saves him from bugs, goimports fixes the problem with required use of packages. It's separate.

The requirement that packages aren't used isn't to prevent bugs, it's to prevent packages from claiming spurious dependencies that you're then too afraid to remove. Or, in the case of a static language like Go, don't want to take the time to remove one-by-one and see if the compilation breaks. I deal with this problem in Perl where, basically, in a source code base that's been developed now for over a decade you have no idea whether a given module is actually used in the code, and it could be pulling in a whole bunch of other stuff very confusingly. The problem is less acute in Go to start with, but it's still nice to be able to rely on the imports being accurate.


"It's separate"

I don't think I misquoted the OP. He said "A and B help me with C" and I questioned how A could help with C, given the other claim that the OP doesn't pay any attention to A anymore.

"Or, in the case of a static language like Go, don't want to take the time to remove one-by-one and see if the compilation breaks"

That is a problem with perl, a language whose 'grammar' is about as free-form as it gets (aside: iOS is funny at autocorrecting programming language names: cobol => cool, perl => peril or pearl, depending on context), but not with go. The compiler can generate errors for unused imports; it easily could generate warnings instead.

I like clean code, but I also like it if a quick experiment doesn't require extra tooling to remove imports to make things compile, more so if it is not guaranteed that undoing such changes can be automated (adding imports isn't 100% reliable; if it were, why have import statements at all?)

In summary: Go is too opiniated here for me.

Also, this requirement effectively rules out ever using go in a repl. I think that is a minus for modern programming languages.


"I don't think I misquoted the OP."

I don't think so either. What he wrote was unclear (sorry, tptacek). I was clarifying what I'm pretty sure he meant. It's not like I've never done the same thing in writing myself.

"Also, this requirement effectively rules out ever using go in a repl. I think that is a minus for modern programming languages."

It's ruled out for a lot of other more fundamental reasons. Go is probably the most highly polished language from the 1980s.

This would be a much greater condemnation, except the sort of people who tend to frequent HN (including me!) tend to grotesquely overestimate how much most programmers are spending coming up to speed on the latest and greatest. In many contexts, a "really nicely polished 1980s language" is a step up in either reliability or speed. Perl is nominally a more advanced language, but on the whole, most programmers just use it as a sort of dynamically-typed C. I think the majority of programmers still aren't all that clear on what a "map" is. Arming my fellow programmers with more capabilities doesn't solve any problem either I or they have, but can make them worse.

Also, I'd observe that even for modern programming languages, "needs a REPL" is setting the bar high. Rust will probably never have a REPL. I see a couple of defunct projects trying and I haven't studied them, but at best Rust could have an all-in-unsafe REPL dialect, because who's going to be able to reliably type borrow-safe code into the REPL on the first try?


Rust and C++ came to mind when I wrote that. That's why I didn't write modern languages _must_, but only called it a minus if they didn't.

And thanks for the relativism in that remark about the most highly polished language from the 1980s. I fear you are right in that (example: today, a colleague said this was the first time he created a thread. He has had a job as a developer for a few years, and, given his age, likely never wrote for a CPU that isn't multi-core. Luckily, he isn't dumb, as he asked what would happen if that thread threw an exception)


> Go is probably the most highly polished language from the 1980s.

I think ML pretty objectively takes that crown, semantically speaking. For simplicity and elegance it's pretty difficult to beat.


I should have said mainstream 1980s. Obviously you can find any number of more sophisticated lanugages in the 1980s by features, give or take the multicore support which, depending on context, was less relevant back then, but not in the mainstream. By that metric ML is still probably "ahead of its time".


goimports will remove imports when they are no longer referenced in the file. So, the otherwise manual step of removing them is automated for you.

Highlighting dead code is not a trivial matter, at least for anything that could be referenced external to the module.


I'm using goimports too, and it works like magic 95% of the time, while I can manually take care of the rest.

But it only solves the unused imports issue. During development (especially when playing with code) I often need to temporarily comment out a section of code. I go build, and the situation usually unfolds like this: - ERROR! Yes, now, I have an unused variable dangling around, because this code was using it. - No problem, let's go on and comment out that variable as well. - Oh noes! That variable was defined using two other variables which are now unused! - The first one comes from a function result, but I still want to call that function, so I need to actually change (not comment out) the calling line to use an underscore. - The second is a global variable which using another variable? Where is it going to end?

At this point I end up following the FAQ's advice and just add the line "_ = unusedVariable". The only problem I have with that is that wasn't all this unused variables thing meant to keep us from shipping code with useless bits we don't need in production? Now I actually might end up not using this variable, but because I've ended up adding "_ = unusedVariable" during my debugging and left that on, I have no way of knowing that.


The unused variables are the things that can really cause bugs in your code. It's annoying, and I share your annoyance, but I have also sighed to myself and said "shit, I'm glad Go caught that" after noticing really bad bugs that the error flagged.


That's why unused variables should be reported.

What people are arguing about is errors vs. warnings.

> things that can really cause bugs

"can" means that they don't always cause bugs: other compilers treat such uncertainties as warnings because the human looking at the screen is the one who is in charge.

Go allows you to compile code which does not respect "go fmt", why? aren't you afraid of committing working code that does not abide to the proper style? Improper formatting can lead to confusion, bugs and maintenance problems too, after all.


As someone else pointed across the thread, the error eliminates these bugs, not only in your code but, effectively, in all the libraries you import. It takes effort to retain those bugs.

That has significant value, and is why I've conclude Go's choice here was the right one, despite the annoyances it causes.

I like gofmt, but I don't really care whether the compiler enforces it. It's unlikely to catch bugs.


Actually, that's a huge speculation. Breaking someone's train of thought with an error that needs fixing right that second could just as well cause some bugs.


And so the compiler, who has no way to make informed decisions, is there to tell you which stylistic issues is more likely to introduce bugs or not, while being needlessly stubborn.

A sufficiently bureaucratic compiler.


>>But it, and, more importantly, the use requirement for variables, has saved me from bugs repeatedly;

So how is it better than C where you get a warning (at least from GCC) that you have unused variables? You can quickly test things and you are coming back to fix all the warnings later anyway.


You get warnings for lots of things in C. There's no getting around the use requirement in Go, at least not without crudding up your code.


I prefer what I do with Python in this regard: The IDE shows me unused variables, I can run my code with them, but I can't commit since the linting hook will produce a hard error at commit-time if something's wrong.

It's not as easy to do with a compiled language, of course, but I don't think it would be over the top to have the compiler exit with a non-success code if there are warnings when building.

The "warning fatigue" you get from C is because the warnings are either about false positives or about things that are too trivial to bother with, not because of a property inherent in compiler warnings themselves.


> I prefer what I do with Python in this regard: The IDE shows me unused variables, I can run my code with them, but I can't commit since the linting hook will produce a hard error at commit-time if something's wrong.

The problem is that while you do that, essentially no-one else on the planet does — and anyone who uses a library thus has to deal with the fact that that library has approximately a 100% chance of having nits like unused variables.


Yes, it's true that sane defaults are supremely important.


Oftentimes when I write in Go, I'll produce an unused variable as a side effect of commenting out code as a debugging experiment. What would you think about Go issuing a warning instead of an error in this case?

Also, if goimports solves import issues, why isn't it just built into the compiler?


It doesn't work as a language feature, because it's not precise or perfect. Package references can be ambiguous, and need to be overridden. It works extremely well as a tool though.


This is a problem specifically about commenting in and out code. The IDE should comment in and out imports accordingly.


Rust warns you about those things; allows for quicker iteration in development, and gives you a list of things to address when you are ready to polish.


There are a lot of languages that warn you about such things. There are also a lot of projects in those languages that spit out a hundreds of warnings when they compile. Are those warnings bugs? Were they examined by someone to make sure they are acceptable? The older the project the less likely the above is true.

Go eliminates an entire class of bugs with this feature. It doesn't just make them detectable and less likely. It eliminates them. If you don't care about eliminating them then Go's philosophy isn't for you.

But I for one am sold on that philosophy. I'm disciplined enough that in my rust code I ensure all warnings are fixed before I release. But I don't know if everyone else is just as disciplined. For Go code I do. Every library I use in Go has an entire class of bugs that are impossible. That's a useful guarantee in my book.

On a side note why do we as developers tend to prioritize making the act of writing code easier than the far more frequent act of maintaining that code? Optimizing writing you code over reading and maintaining that code long term seems like a case of premature optimization to me. Allowing something like warnings is one of those things that adds overhead to writing code but eliminates overhead over the life of maintaining that code. The gains are higher longer term at the expense of a shorter term pain. And a pain that tooling like goimports can almost completely eliminate in almost all cases.


> On a side note why do we as developers tend to prioritize making the act of writing code easier than the far more frequent act of maintaining that code?

Different phases of the development process call for different priorities: in early dev, you want reduced friction and quick turn-around time (something that the unsed imports and variables errors in Go infringe upon). Later on, you want maintainability and ensure that things don't randomly break (at this point, those errors become quite desirable). We shouldn't emphasize readers over writers all the time, we should emphasize them when it's the correct time to do so, which I'll admit is the majority of the time.

> Optimizing writing you code over reading and maintaining that code long term seems like a case of premature optimization to me.

That's actually something I've been thinking about quite a bit in the past few weeks :) I think if we were really concerned about readability of code, we would have much better literate programming tools (e.g., better integration with existing dev tools) and our programs would be written with the intent of being read by others. How many times have we felt lost in a foreign code base (or worse, a code base we wrote, but don't remember too well) trying to implement a change and not knowing or understanding some of the design decisions, being unaware of key assumptions, etc. There is a lot more to readability and understandability than having functions without unused locals. (By the way, why is it okay for a function to have unused input parameters?)


    (By the way, why is it okay for a function to have 
     unused input parameters?)
That's caused by Go's interfaces and function type signatures. It may be necessary for a method or function to accept certain arguments to satisfy an interface or type signature even when those arguments don't get used. It's a bit of a wort I agree.


> There are also a lot of projects in those languages that spit out a hundreds of warnings when they compile. Are those warnings bugs? Were they examined by someone to make sure they are acceptable? The older the project the less likely the above is true.

The amount of development time I save by allowing warnings temporarily when testing easily outweighs any benefit to the ecosystem derived from making that warning abort compilation. I was partially responsible for making this a warning instead of a hard error in Rust and this wasn't even close to a tough call. I would be surprised if a single project in the ecosystem has ever benefited from this: projects that care about software quality pay attention to warnings, and those that don't care about this minimum standard of quality are going to be full of so many other bugs that the unused variable/import warning will make no difference.

> Go eliminates an entire class of bugs with this feature. It doesn't just make them detectable and less likely. It eliminates them. If you don't care about eliminating them then Go's philosophy isn't for you.

Go's philosophy is not about eliminating bugs above all else. If it were, then for example zero values wouldn't exist, reading from a nil map would panic, constants would have stronger types, all fields would have to be supplied when initializing structs, init ordering wouldn't be undefined, overflow would trap, errors would be required to be handled, and that's just off the top of my head.


I thought it ironic that go also has a "go vet" which spits out "warnings" but...are those warnings bugs? Were they examined? etc. etc. :|


Fortunately, I don't see this as a big issue in Go so far because I have used it for small components connected by zeromq or http. In a way, Go is also enforcing distributed/microservice style of development since a huge monolithical/business-rules program with it seems like nightmare material.


I use an Atom extension called "gofmt" which automatically calls goimports[1] every time I save. So any unused imports will be removed and if I start to use new imports that are in my GOPATH or the standard library then it automatically adds them.

[1] https://godoc.org/golang.org/x/tools/cmd/goimports


How does that work with undo?


I can't speak for Atom, but here's what it's like in VIM: http://i.imgur.com/GkP4Yk9.gif


Works the same in Atom.


goimports guesses the one you want and adds it back.


My question was, since goimports modifies your document (and gofmt even more so), is your undo stack preserved across saves? If so, how?


In Eclipse, this is true. The import was considered an "action" that got put into your undo stack. It was just triggered by the editor, not you.


That's a question for your text editor.

I use Sublime Text 3 with GoSublime and have noticed that the undo stack is just my changes (makes sense, the goimports is an external process), but that's cool because any CTRL+S is going to trigger goimports and so the correction of the imports is invisible and always there.


I believe it has worked well for me for undo. However some other Atom formatters have broken my undo in weird ways, namely the formatter for Node JS I had turned on to auto format on saves. I had to turn it off.


i was shocked by that at first too. Then I came to love it. It's sooooo go. Don't import stuff you aren't using. Don't declare a var and just leave it there un-used. When I go back to ruby and commit that crime, I see why golang did what it did. There's something magical about a golang program that will compile without error. It's worthy of checking in to git. And months later, there will be no unsed imports FOR SURE.


Stockholm syndrome in action. There are two phases: active development and release engineering. -Wall for former, -Wextra -Werror for latter. If someone is not disciplined enough to throw out unused variables/imports/whatever from release, Go will not save him from hell.


> Stockholm syndrome in action.

That's a very negative way of saying "that's not my personal behavioral preference"

> There are two phases: active development and release engineering. -Wall for former, -Wextra -Werror for latter.

Many of Go's idiosyncrasies are for code readability and maintainability, which is very much a part of active development. The point being to mitigate the likelihood you end up with spaghetti code (yes, I know you ultimately end up with code quality only as good as the developer, but some languages do still make the process easier than others)

> If someone is not disciplined enough to throw out unused variables/imports/whatever from release, Go will not save him from hell.

Except it does though, since that's the very behaviour you're complaining about.


>> Stockholm syndrome in action.

>That's a very negative way of saying "that's not my personal behavioral preference"

In my experience, Stockholm syndrome is not used to describe personal behavioral preferences, but rather (unexpected) transitions between such preferences from the negative to the positive.

In the grandparent's own words:

>>> i was shocked by that at first too. Then I came to love it.


It's possible that's what he meant, but that's not how I understand the term.

Stockholm syndrome refers is a victim forming emotional bonding with his or her captor[1]. Which would mean in instances like this, where the "captor" is a voluntary choice of language, the term is used akin to "masochism" (albeit without the sexual element). In essence, saying "the pleasure from use feature x is derived from the pain of using feature x".

Which is why I commented that "Stockholm syndrome" is a needlessly colourful way if saying "I disagree".

[1] https://en.wikipedia.org/wiki/Stockholm_syndrome


The "masochism" aspect makes Go part of Bondage and Discipline languages:

http://c2.com/cgi/wiki?BondageAndDisciplineLanguage


Yes, but it requires that the victim has no way out. Stockholm syndrome is not just any unexpected change of preferences.


The victim has no way out short of switching languages which may or may not be under their control.


Stockholm syndrome, as far as I know, was observed in situations of physical threat to life or health with the only way out being death.


> Except it does though, since that's the very behaviour you're complaining about.

No, the meaning was: if someone cannot handle unused things, that someone is screwed. Go will not save him in other aspects which require a little bit of discipline and are not checked by the compiler.


> if someone cannot handle unused things, that someone is screwed.

So what you're saying is, there are two kinds of people—those who don't need the feature, and those for whom the feature isn't sufficient?


User laumars was apparently misinterpreting part of a comment by saying that "unused" errors (not warnings, but errors) would in fact save the hypothetical "someone" that was claimed to be not disciplined enough to remove unused imports at release time. But the parent comment was in fact saying that the disability to handle something as simple as unused imports would prevent said programmer to accomplish the less trivial things that programmers are expected to do and for which no tools is a substitute for.

My personal view on this is that warning the user should be enough. The compiler designer is basically saying: I can't trust developers to fix warnings and it is my duty to protect them from their own laziness by enforcing a certain way of working. This apparently attracts a certain kind of people. I prefer to be the one telling the compiler what to do, instead of the reverse.


> My personal view on this is that warning the user should be enough. The compiler designer is basically saying: I can't trust developers to fix warnings and it is my duty to protect them from their own laziness by enforcing a certain way of working. This apparently attracts a certain kind of people. I prefer to be the one telling the compiler what to do, instead of the reverse.

I'm with you there, I just was making a snarky comment :-)


This is literally the "perfect is the enemy of the good" fallacy.


    > There are two phases: active development and release 
    > engineering.
For you, perhaps. Go encourages you to think about your problem and develop it in the "release engineering" style from the very beginning. This is a virtue of the language, in my view.


Ada is probably the most real-software-engineers-dont-hack language ever designed, and yet compilers have flags to control checks and warnings.


No, there is no encouragement here. Go forces you to treat things that have always been just warnings in other languages as errors. And I wouldn't call a virtue something that doesn't give you a choice. I will be really pissed off if the language thinks that I'm so idiot that I need errors instead of simple warnings for the cases discussed here.


You are human, humans forget, humans get distracted, humans think that their situation is the exception that makes the rule.

A program doesn't suffer from that. Forcing these things to be showstoppers makes sure they NEVER become a problem, and the "cost" (in terms of the developer) is pretty small in the grand scheme of things.


How in the world forcing someone to put an underscore before the import name or adding a dummy variable to use the import (both should be removed after the debugging) helps to avoid bugs instead of making easier to introduce them?


Adding a dummy var won't work, because then the dummy var will trip the "no unused variables" check.

Adding a _ to the front of an import is not the correct way to get around this "problem" during development, commenting out the whole import is the correct way. That way if you forget, it's not in your code anywhere (as it shouldn't be since it isn't used).

The underscore system is meant only to be used when you need to import for side effects (something that i've never actually needed to use).


I have seen both suggestions in this thread: var _ = unusedImport

If you can't even agree among yourselves what is the right thing to do then I think that this is another sign that this go behaviour is not the proper solution.


If you have seen other people suggest that using a _ for an unused import that is unused because you commented out the variables using it, they are just doing more work for a worse result.

If you comment out code that uses an import, comment out the import too. It's not just the recommended way, it's the only logical way of doing things.

And how is having 2 opinions on how to do something a sign that this is not the proper solution? People still can't agree on tabs vs spaces (and probably never will be able to), that doesn't mean that all programming isn't a proper solution. There are also people that think using version control is a bad idea, but that doesn't mean that all version control is broken...


> Stockholm syndrome in action.

So here's a language feature that you don't like. Other people don't like it and then, after they get more experience, decide they like it after all. And you call it Stockholm syndrome? By saying that, you're asserting that there is no possibility that the feature could possibly be anything other than harmful. That's... pretty arrogant, actually. Both the language designers and the users who like it are objectively wrong, and you're right?

Look, you don't like it. That's fine. It doesn't work with the way you like to develop. That's fine, too. But your assertion is that it is worse than useless for all users, and that's a bit more than you have the knowledge to assert.


This may work for you code. But are you sure that all the libraries you use also follow that rule? Are you going to audit all of those projects and their dependencies to validate they follow the same best practices as you?

The end result of Go's opinion on warnings is that you know a certain class of bugs in not present anywhere in both your code and all of your dependencies. While it's true that people and teams that follow the best practices around warnings also tend to follow the best practices for other things but there is no guarantee for the ecosystem in a language that allows warnings, and no project lives in a vacuum.


This is an easy problem to solve.

The Go compiler could simply include a bit in the resulting binary/library that indicates whether or not it was built with the "disable-unused-variable-error" flag. If you're using a library that was built with that flag the compiler can simply refuse to compile your code without that flag, forcing you to acknowledge the problem head-on.


This doesn't solve the problem. It just makes you aware it might be there. It doesn't keep the ecosystem clean.

The result will be that everyone compiles with that flag on so they can use the libraries they want/need. It's in fact actively worse than just allowing warnings. At least if you allow warnings that way you aren't forced to lower your own codes protection just because one of your dependencies does.


Complaining about unused import doesn't solve anything IRL, if we talking about solving something. Problems in code are complex diseases, and cleaning unused entities is like washing your hands, not like taking a cure. And hand cleanliness checks is too idiotic measure to cure brain cancer. Of course all our problems are still here in both cases, but at least with Go we will die clean for sure.


If a library is so popular that "everyone" uses the flag so they can use it, surely someone can be convinced to remove the unused import statement. If the ecosystem makes that difficult then we're talking about a totally different problem.


Why on earth would the language hinder you from iterating fast with a flag, and then convert all the warnings to errors when you run the full tests?

For example, most JS linters do that check and as a general rule, the build servers would reject the commit when the linter complains. I actually have a (very hacky) plugin to ESLint that automatically removes unused variables before `hg push`. It's totally not optimal, as in, you can still run/compile/transpile/whateverify the code with default options, but a language with no backwards compatibility worries can just add a flag to override strict checks to make a developers life easier, can't it?

I understand not implementing something that perhaps may not fit the language well or is so complicated that it really needs a lot of thought (read: generics). But this decision seems to be dogmatic at best.

I have the exact love/hate relationship with Go as the author of the article.


Devil's advocate: if you can disable -Werror, then users will disable -Werror, and we'll end up in a C-like situation where everything warns all the time, and real problems get buried. Better to inflict some pain during development if it keeps the Go source corpus hygienic.

One under-appreciated consequence is that this policy limits Go to one implementation. Say I write an alternative implementation of Go, that has a different "unreachable code" detector. If I issue errors on new cases, then I'm incompatible with lots of existing Go code. If I fail to issue errors, then code developed against me may be incompatible with the standard Go. The effect is to suppress alternative implementations.

Better to just put my smarts into a linter, which is then just reinventing compiler warnings under a different guise.


> Devil's advocate: if you can disable -Werror, then users will disable -Werror, and we'll end up in a C-like situation where everything warns all the time, and real problems get buried.

I already mentioned that case. To be more clear: Let it not build with default options. Make it not `go get`able.

Let me develop first, and not force me to optimize prematurely.


Enforcing -Werror at the "go get" phase would achieve 90% of the benefit with 10% of the pain. It would be a big improvement.


> Let me develop first, and not force me to optimize prematurely.

I'd argue that cleaning unused code as you work isn't optimisation. It's a part of the refactoring process of development which is something that must happen during the main development phase.

But arguments for and against aside, most developers don't see these kinds of compiler errors that often as you'd generally be commenting out unwanted code while you're prototyping. The biggest annoyance is easily the imports and pragmatically that still takes up such an insignificant amount of effort when compared with the overall time spent writing code in any particular language. Which is why many Go advocates are willing to put up with it.


> It's a part of the refactoring process of development which is something that must happen during the main development phase

How is it refactoring if my code never builds in the first place?

> pragmatically that still takes up such an insignificant amount of effort when compared with the overall time spent writing code in any particular language. Which is why many Go advocates are willing to put up with it.

It's not a time problem. It's a focus problem. I'm working on something and then out of nowhere, receive compilation errors, which aren't urgent at all, but they distract me anyway. Even if this were indeed a very small problem, I still don't see why something most people see as a problem is pushed down the developers throat with false, dogmatic reasoning. Don't get me wrong: I have huge respect to the people who created go. I just don't like when people defend all the choices they made as "one true way".


> How is it refactoring if my code never builds in the first place?

Why would you be typing in imports that you've never used? I guess that could happen, but the more likely scenario you had used them at some point, even if just briefly, and then you're refactoring the code.

> It's not a time problem. It's a focus problem. I'm working on something and then out of nowhere, receive compilation errors, [...] pushed down the developers throat with false, dogmatic reasoning

Please don't be so melodramatic. It doesn't happen "out of nowhere" - you've purposely requested the compiler runs. And if you're compiling then you're doing so to either test your code compiles or test it runs - in either case your focus has already intentionally shifted from writing code to testing and debugging it.

> Don't get me wrong: I have huge respect to the people who created go. I just don't like when people defend all the choices they made as "one true way".

I'm not defending it. I'm saying people are being melodramatic about the actual inconvenience it causes. In all of the years I've been writing Go, I think I've spent just as much time coding around that annoyance than I've spent in this thread chatting about it. The difference being, occasionally that annoyance is useful where as the internet arguments about it literally serves no benefit to anyone.


> Please don't be so melodramatic. It doesn't happen "out of nowhere". You've purposely broken your development focus to compile and unit test.

No, I'm compiling to see if my changes still allow the tests to pass. This is a huge problem if you're doing TDD. The main argument you make is that it's not such a big problem and I think the number of people complaining says otherwise. Or maybe we developers just like being melodramatic. I honestly can't tell. Maybe what triggers such passion is go being very close to a perfect tool for certain things and those annoyances look like small black dots in a perfectly white surface (That sounded dramatic as well, what's wrong with me? :) )


I don't have a great amount of experience with TDD specifically, so maybe I am underestimating the inconvenience this causes some developers. :)


>Devil's advocate: if you can disable -Werror, then users will disable -Werror, and we'll end up in a C-like situation where everything warns all the time, and real problems get buried.

Isn't it implied that programmers are adults?


Go's entire philosophy is that developers are a bunch of incompetent slobs, and it's the language's job to fix that.

I can't say I blame Rob Pike for not trusting developers, and I've definitely seen more than one case of a C/C++ codebase riddled with thousands of compiler warnings that were just ignored en masse during compilation.

There is another side of the coin, which is that experienced and disciplined developers who think they can take care of themselves, thankyouverymuch, would end up feeling limited by Go.


> Isn't it implied that programmers are adults?

It's one of those irregular situations:

I'm a senior developer.

You're an adult programmer.

He's a stark raving juvenile lunatic.


You set things up so you can't disable -Werror on the build server, and all this is moot.

The point about alternative implementations is interesting. Java has a comparable situation with unreachable code; the language specification has a simplistic definition of what unreachable code is, and makes it a compile-time error. A smart compiler can find more cases of unreachable code, but isn't allowed to make them errors. So it makes them warnings. It doesn't seem to be a big impediment to alternative implementations in practice.


Why on earth would the language hinder you from iterating fast with a flag, and then convert all the warnings to errors when you run the full tests?

There is a cost to this too. It is another option. Another thing to document and maintain. Another thing for developers to learn about. Another thing for newbies to be confused by. Another thing that will asked about on the mailing list.


Define unused imports/variables as warnings but produce an executable that first prints "This program is for debugging purposes only, it built with 133 warnings." and beeps loudly.


Nothing like a little public shaming to keep your code in check.


What about just really obnoxious compiler warnings that can't be disabled?


More than once I've built some free software from source, only to witness literally thousands of warnings being spit out during the compile phase.


I would argue that that's becuase checks had to be enabled not because they could be disabled.


Unused Ruby variables stand out like a sore thumb, if you have a linter configured with your editor. I use emacs with rubocop through flycheck.


Not allowing the import of unused packages was an explicit design goal intended to reduce compile times. Cribbing from a comment I made years ago:

Take a look at this adaptation of a keynote talk on the design of Go, with the title "Language Design in the Service of Software Engineering": http://talks.golang.org/2012/splash.article Section 7, "Dependencies in Go" addresses this issue: http://talks.golang.org/2012/splash.article#TOC_7. The relevant paragraph states,

The first step to making Go scale, dependency-wise, is that the language defines that unused dependencies are a compile-time error (not a warning, an error). If the source file imports a package it does not use, the program will not compile. This guarantees by construction that the dependency tree for any Go program is precise, that it has no extraneous edges. That, in turn, guarantees that no extra code will be compiled when building the program, which minimizes compilation time.

Further, the talk justifies this approach through their analysis of how their C++ codebase was compiled:

The construction of a single C++ binary at Google can open and read hundreds of individual header files tens of thousands of times. In 2007, build engineers at Google instrumented the compilation of a major Google binary. The file contained about two thousand files that, if simply concatenated together, totaled 4.2 megabytes. By the time the #includes had been expanded, over 8 gigabytes were being delivered to the input of the compiler, a blow-up of 2000 bytes for every C++ source byte.

As another data point, in 2003 Google's build system was moved from a single Makefile to a per-directory design with better-managed, more explicit dependencies. A typical binary shrank about 40% in file size, just from having more accurate dependencies recorded. Even so, the properties of C++ (or C for that matter) make it impractical to verify those dependencies automatically, and today we still do not have an accurate understanding of the dependency requirements of large Google C++ binaries.


I really would like to see what happens when somebody forks go to create a sort of Go++ (tm) which, while still compiling code written for actual Go, didn't strictly prohibit unused variables and imports but only issued warnings and, added function templating as a means of generics. Regardless of whether these are good ideas or not to have in Golang, it'd be interesting to follow how the community orientates itself in time.


Well, there's Oden experimental language that compiles to Go, but it's Haskell inspired (even written in Haskell itself) https://oden-lang.org


You may want to configure your editor to use goimports: https://godoc.org/golang.org/x/tools/cmd/goimports


I think that's the thing that really gets me about Go. I really like that it's opinionated and forces you to write good code, but I just can't understand why they refuse to allow you to turn them off when you need it.

Also doesn't that workaround completely nullify all the arguments they made in that section? And it puts the programmer in a worse position than if they could just turn it off temporarily.


"I just can't understand why they refuse to allow you to turn them off when you need it."

The answer to this and a lot of similar such questions about Go is "scale". Things that were sorta kinda convenient for you in a couple hundred lines become nightmares when several dozen developers on several hundred thousands lines of codes had differing ideas of what was convenient for them at the time.

Just as you can't understand Erlang until you understand its emphasis on reliability, you can't understand Go until you understand its emphasis on coding at scale. Almost (but not quite) everything people can't understand about Go are things that people want to do because they work well locally, but things that have become serious problems in source code bases at scale. You also can't understand Go until you realize that the problems weren't merely hypothesized, but extracted from real already-existing source code bases.

That said, I still feel they missed some steps. I'd like non-nil-able pointers. (Optional, like C#, because the way Go methods work means that if you're careful nil pointers of a certain type are perfectly legal and can have sensible methods on them, but that's not always the case.) Packaging is another obvious case where Google had an answer that worked for them so they didn't see a problem, though that's being worked on. (I'm struggling through this because I consider it a non-negotiable feature that my packaging solution allows me to locally mirror everything I use, which all packaging systems (not just Go!) seem to consider some bizarre use case. Strange, I consider it a basic requirement for truly professional development.) So bear in mind I don't mean this necessarily as a defense of Go, I'm talking about what it takes to understand it.

(Edit: also, yes, I consider it a bit sad that Go doesn't have iterator support, per my comment at https://news.ycombinator.com/item?id=12210351 You can sort of bash some stuff together, and io.Writer and io.Reader cover a common case for Go programs and can be composed, but it's still a bummer.)

It is completely the case that some people will come to a greater understanding of Go, and realize it's the wrong choice for their problems. But perhaps it will, at the very least, be less threatening that other people do consider it a good choice for their problems.


I use an editor plugin that adds and removes imports automatically. It works very well.

https://github.com/DisposaBoy/GoSublime


It uses goimports for that.


I think it depends on what you're using it for. On my day-to-day I'm working with absolutely massive pieces of software that have dependency trees that would literally make me cry if I stopped and thought about it too hard.

In that world, this sort of thing enforced strongly makes me much happier.

For random one-off hacking, yea, it'd be a pain.

Differing use cases lend cause to different tooling.


People are ok with compilers telling them what is wrong, they complain that Go wants to control when you fix the problem: "fix that import NOW, bad developer" is not opinionated, it is stupid.

> Differing use cases lend cause to different tooling.

What?


By differing use cases:

If you're messing around with your own test code, it's fine if your compiles get slower, etc. On the other hand, if you're writing code that thousands of other engineers are immediately going to depend on, it's better if you're not allowed at all to make things slower via unused imports. True, if you could globally mandate the use of a lint/presubmit rule disallowing unused imports, that would also work. But, that's a harder swing.


My reaction to "differing use cases lend to different tools" was that tools and especially languages often accommodate multiple, sometimes contradictory use cases.

I am not writing code that thousands of other engineers are going to depend on. A company that requires this can enforce methodological rules. Go is supposed to be used in different contexts, not just Google, right?


> > Differing use cases lend cause to different tooling.

> What?

Different needs, different languages.

I write 100-line scripts in Perl that rip apart text files and extract the bits I want. I don't write 100,000 line embedded systems in Perl.


What about different needs among the users of a same language?


Any language satisfies some needs well (hopefully), and some needs less well, and some other needs badly. Different languages have different sets of needs that they satisfy well.

So, if you're complaining that a language doesn't satisfy a need well, then go find one that satisfies it better. Use that instead.


It's a simple reflection of the fact that the Go creators have never used a modern IDE.

Anyone using a modern IDE pretty much never even thinks about imports, which are (and should be) automatically managed by the tool, not by the programmer.


This statement is probably true but that's kind of irrelevant. IMO it is actually a virtue of the language if it is perfectly usable without a modern IDE. Contrast that with Java and you'll see what I mean. Go follows the Unix philosophy where things like imports or formatting can be supplied by any external tool. If you wish to bundle all these small tools into a single IDE you can do it, too.


Everything is perfectly usable without an IDE, you are just more productive with one than without one. Regardless of the language.

In this particular case, the IDE could automatically remove the imports when you remove the corresponding symbol from your source, or automatically add the import when you introduce a new symbol.

The "can be supplied by any external tool" is a cop out that was acceptable in the 20th century but this is 2016, we have a higher bar for developer productivity.


Nothing about the language or its tooling prevents you from building such an IDE or a plugin to one of the existing IDEs. And there is absolutely nothing in Java that makes IntelliJ so awesome. These things are totally orthogonal. What I like about Go's tooling is that it allows good plugins for various text editors (eg. Sublime or Atom) to be implemented with relatively little effort.


> Nothing about the language or its tooling prevents you from building such an IDE or a plugin to one of the existing IDEs. And there is absolutely nothing in Java that makes IntelliJ so awesome. These things are totally orthogonal.

No, they're not.

There are a few necessary conditions for this kind of symbiosis to work well:

- The language has to be statically typed. Without type information, the IDE is completely blind and can barely help you at all without human supervision.

- The IDE has to be written on top of the same runtime as the language it's editing. This is what makes Eclipse/IDEA and Visual Studio so spectacular: they understand the bytecode they are working with.

These are necessary conditions but they're not sufficient. You can still write crappy IDE's if these conditions are met, but thankfully, IDEA/Eclipse/Visual Studio are technical wonders that multiply the productivity of their users.

For example, in Go, the simple action of selecting the the surrounding expression simply doesn't exist anywhere. It's the most basic automatic action that's trivial to do with the proper language and IDE, and yet nonexistent in Go. Along with tens of others.

That's the price to pay when a language is designed without any consideration for its tooling nor basic things we learned about language design these past twenty years.


> The language has to be statically typed

Go is statically typed.

> the simple action of selecting the the surrounding expression simply doesn't exist anywhere

How so? It's trivial with the `go/ast` package.


Does any Go editor support that?


Is it relevant to the question at hand? Languages != their IDEs.


Assuming emacs doesn't qualify as a "modern" IDE. I have a hook that calls go imports to remove and add imports.


Bikeshedding much? I can't see this being a real problem. There are a lot of other decision that the Go team made, for better or for worse that have much greater effects on real code. Unused imports is a joke.

var _ = unusedImport

Or use goimports. Then you don't even have to think about it.


(I've never used Go, so please correct any errors.)

> var _ = unusedImport

This is what the devs suggest as an alternative to having a compiler flag to turn off the check.

But with a compiler flag, I can easily discover the unused import when I build for production, simply by turning that flag off. By using the variable in a dummy way, I need to remember to go back and check for such things.

Presenting this as a way to address the problem feels like it's missing the point.


In practice you don't really have this problem. It's a beginners issue thst's not worth "fixing" because there are tools that can help(goimports). I'm glad there are not such compiler flags.


Goimports doesn't help when the name is ambiguous.

>I'm glad there are not such compiler flags.

Because if they were there people would force you to use them? Or are you just unhappy that people could have a different debugging workflow than you?


> Goimports doesn't help when the name is ambiguous.

You can fork it and set it to only remove unused imports. In fact I've done just that few days ago but for different reasons(i.e. I generate some small programs and I use goimports as a quick and dirty way to to clean-up the unused imports). Here is the line where you need to return https://github.com/golang/tools/blob/master/imports/fix.go#L...

> Because if they were there people would force you to use them? Or are you just unhappy that people could have a different debugging workflow than you?

Yeah, I don't want being forced to use a dozen of flags just to compile a hello world program. Nor I do what to see various warnings that nobody really care about. It should fail or succeed. I prefer basic/minimal API that I can build upon not a kitchen sink approach. I think that's what makes Go a great ecosystem compared with many others. Compare the current Go compiler with various C++ compilers and you understand what I mean.

To sum it up you may use separate tools to do various magic things (i.e. goimports), they do not need to obey the Go1 compatibility rules. The issue is not that big to warrant a compiler flag.


> You can fork it and set it to RemoveOnly.

This seems like it removes half the value of the tool, though. I can comment out a line of code, compile and test the program. But if I uncomment that line, I'm now missing an import.

edit- how does a tool like this sound?

* If it sees an unused import, it comments it out

* If it sees an import is needed, and there's a commented-out import which would fit, it uncomments it.

* It can run in a "check for commented-out imports" mode, so that commit hooks can reject such a state.

I guess (making some assumptions about go's import semantics) this would avoid the ambiguous name problem; it would mean you avoid having to edit both the import and the usage at the same time; and it makes it easy to tell if a file should be cleaned up.

Maybe it would cause issues if you're trying to choose between two libraries which both provide the same symbols?


Well, that's the point! If you don't want inaccurate imports and just want to remove unused imports you can comment that line. A better option would be to use an additional argument to that function (removeOnly bool) and perhaps a flag on the executable so that you can choose when to use the removeOnly option. Either way the automatic imports and even the removeOnly option alone requires various trade-offs(i.e. accuracy, compiler speed etc) and this is another reason why such features are better developed in stand alone/external tools.


>Then you don't even have to think about it.

Can't agree with that. We once had an issue with our code because someone had made a copy of his code base and goimports was importing an older version of a library.

Yes definitely this is something the programmer could have prevented. BUT! "not think about it" is not really true.


Ok. Effectively don't have to think about it 95% of the time then. Close enough.


Everyone else is suggesting goimports, but here's another trick:

    import _ "unused/package"

This is usually used to benefit from the packages' side-effects (e.g. trigger init() - mostly used in SQL drivers to register the driver).


But then you have to remove blank identifier to actually use package.


> The most frustrating thing for me, by far, is that Go won't let you import unused packages.

If you want to explicitly import unused packages, you just prefix it with `_`, like this: https://play.golang.org/p/ltfQgco22t

Remove the `_` in the import of `crypto/rand`, and the program won't compile anymore.


Meh, I just have emacs configured to run goimports on save, right along with gofmt. I never even worry about, unless I have to resolve which of two packages with the same final name component I'm using.

That can be a bit annoying, but it's not terrible. Back when Go had insanely great compile times, it was worth it. Now that compile times are … okay … it's — okay.


This also infuriates me (more-so the "know-it-all" attitudes of the devs). What's even more infuriating is how simple of a change this is. It took me about 15 mins to change the compiler, adding compiler warnings and producing a warning instead of an error on unused imports and vars.


There's at least one tool to help with that, maybe even more. I have never, not once, manually removed an unused import in the last year of Go programming.

Basically, as soon as I save my file, it gets formatted, linted, unused imports are removed, the project is then compiled and unit tests are ran. I couldn't be happier.


I use goimports.


Can't you just also comment out the package import declaration?


Oh. Go love-hate relationship post. My turn. So I really like channels and coroutines built into language and used everywhere: it makes some patterns compose nicely and language very productive.

But just as in the article - some of Golang choices are opinionated and IMO just stupid. Lack of assertions is one: Sure programmers are prone to ignoring errors, but Go is not helping at all. Just bans assertions and provides no improvements. No `try!`&`Result` like Rust, not even warning by default if you ignored returned error (IIRC, go lint or go vet checks it) . So what's the point?

How do you decrement an atomic in Go?

AddUint32(&x, ^uint32(c-1))

That's how. Easy to read, and self-explaining. https://golang.org/pkg/sync/atomic/ . I mean... come on.

Unused errors being an error is super annoying, and changing variable capitalization everywhere, because you want to change symbol visibility is another daily frustration source.

Oh, and all my contacts with the community were very unpleasant (maybe it's just me, or bad luck). Any questions ended with some form of "you must do it one true way, otherwise you're stupid". Eg. once I was looking for a way to terminate unit test in case of error, instead of writing `if err != nil { t.Errorf(...) }` over and over again after every line, as in test, every error is just terminating condition. In Rust I would just `.unwrap()` or `.expect()`, which would fail the test, report backtrace, etc if anything unexpected went wrong. All the help that I got was just snarky comments for being lazy.

I will continue to use Go in some projects, as it gets the job done, performance is OK, has some userbase, and ecosystem is quite vital, but overall, it seems to me that it's rooted in same form of stubborn crudeness that has no good reason in XXI century, ignoring years of research and good ideas. For anything more demanding, or fun I would always go with Rust.


> ignoring years of research and good ideas

And people are falling over themselves to use Go in places that seem inappropriate. Most of Go's strengths shine in a large team of mediocre developers: Low build times, low abstraction, quick ramp-up, etc. So why do startups choose Go? You're just shooting yourself in the foot. Use OCaml, use C++, use Clojure, hell, use Swift or Rust, just use something that at least pretends to care about expressiveness and abstraction. I actively avoid working for shops that use Go heavily, it's an indicator of cargo-culting.


I run a few side projects with a pool of about a dozen developers. Some love Java but others won't touch it, some are C developers, one of them only has experience with PHP, there are lispers, an Erlang zealot and even a Delphi refugee. I personally like Perl and OCaml but couldn't convince them to even consider them. It was a nightmare to agree and settle up with something.

Enter Go, and while we all agreed that the language kind of sucks and $FAV_LANG is better, we somehow stopped bike-shedding and started to get real work done.

Sure, it's not the most exciting language in the block and its community seems to live in the 70s, but sometimes targeting the lowest common denominator can pay off, as is the case for us.


> Sure, it's not the most exciting language in the block and its community seems to live in the 70s, but sometimes targeting the lowest common denominator can pay off, as is the case for us.

Except that even Algol 68 is more feature rich than Go, assuming a 60's powerful computer, like the Burroughs B5000. :)

But I do appreciate that every line of written Go code is one less of written C code.


Define "feature".

Go thinks fast compile time is a feature. How fast is your Algol 68 compiler on a ten million line code base?


1. How many people have 10 million line code bases?

2. How many of those people would have much smaller codebases if they used a more expressive language?

3. Of the people who have 10 million line codebases and would still have 10 million line codebases in a more expressive language, how many of those could actually compile their programs faster if they used a language that allowed partial compilation of only the parts of the code that were changed?

Very few people have Google's problems, and I'm not even completely convinced that Go is the best way to solve Google's problems.


Fast enough for a 60's computer, imagine nowadays.

But if you want numbers, Turbo Pascal 5.5 was doing 34,000 lines/minute[1] on MS-DOS back in 1989.

[1] http://edn.embarcadero.com/article/20803


Turbo Pascal isn't Algol 68. A PC in 1989 isn't a B5000. But even accepting the example, 34,000 lines/minute gets you a compile that takes 294 minutes, or just under 5 hours. That's... not very good.

Of course faster hardware is part of the answer, but I suspect that it doesn't cover nearly all the ground between Turbo Pascal's compile time and Go's.


I gave Turbo Pascal as an example, because even when compared against it, there are many features that Go lacks and Turbo Pascal had.

Besides I am comparing an 8086 with 640 KB against a i7 with 16 GB and three level caches.

Go being fast isn't that much of an achievement in 2016.

If Go compiles faster than Turbo Pascal with an 8086 CPU and 640 KB RAM, that is an achievement.


I am going to guess that Go compiles at least 10 times faster than Turbo Pascal. From the numbers you gave, I think at least a factor of ten is left even after you account for the difference in hardware speeds.


So you are stating that Go will compile 10x faster than Turbo Pascal on a 8086 with 640 KB?!

My point is that Go's compilation speed isn't nothing extraordinary, I can keep giving examples of other compiled languages that had equally fast compilers in the the mid-90's.

Oberon, Go's grand daddy could bootstrap itself and build a full working graphical workstation OS in less than one minute if I remember correctly.


Sounds like a prison for programmers.


I think it really depends on what kind of software you're writing. At my startup (around 30 engineers) most of what we do is web based, and our backends are mostly written with Java and Spring Boot now. Go is simpler, more productive, has better concurrency, and still has good static analysis. The benefits mostly seem to come from the removal of bullshit rather than the addition of nice abstractions.


You don't know what you are talking about. Have you seen any Swift backend used in production? Would you use C++ for a web API? Go was developed because C++ is a horibble language and I'm glad it didn't adopt the expressiveness and abstraction from C++.


>You don't know what you are talking about. Have you seen any Swift backend used in production?

He said "hell, use Swift" -- which didn't imply that he's considering it the first or best recourse.

That said:

1) There's far more Swift in production (on iOS devices as native apps, not as backend, but still production code) than Go.

2) Merely 4-5 years ago nobody had seen Go in backend (web/server) production either. Or at least very few.

3) There's already some support for server side Swift: http://perfect.org/

4) And more: IBM uses Swift for apps AND server side, and has open sourced a Swift web framework/server: https://github.com/ibm-swift/kitura

>Would you use C++ for a web API?

If I needed the crude speed yes, and in fact lots of web APIs delegate to C++ behind the scenes (e.g. for task queues), whereas some are in C++ directly.

In fact Facebook itself used to transpile PHP to C++ and run that (through their HipHop project).

>Go was developed because C++ is a horrible language

Go was developed because some old C/UNIX hackers didn't much like C++/Java and wanted to do their own thing, bringing some Plan 9 flavor on.

C++, warts and all, has proven itself time and again.

Most of the things we depend on and use, from the browser you're using, to JITS and compilers (LLVM for one, V8), to the Windows OS, nearly all major GUI programs (all major DAWs, NLEs, Photoshop, 3D programs, KDEetc), to 99% of AAA games, to Google's search engine core are written in C++.

And C++11/C++14 standards have made it a much better, even new, language. The automatic C++ dislike is cargo cult from people who usually don't use it and just repeat old wives tales.


>He said "hell, use Swift" -- which didn't imply that he's considering it the first or best recourse.

I've got that but it was too late... the comment was already posted. I've up-voted his response.

The rest of your points are invalid. Development is not only about a http server(which I'm sure is subpar compared with Go). You will burry your time and energy in making basic things work. As developer that's great because you can use your experience on your "next" project but as a start-up you are very likely to fail. There may be some IBM developers that played around with the language but I'm sure IBM has no swift powered service in production. Swift is still a language in flux with many changes pending in the next 2 -3 versions. Big corporations don't really like to invest in unstable environments. Maybe in 3-4 years from now Swift will be able to compete with Go but today there is really no reason to use Swift over Go for a backend service.

> Go was developed because some old C/UNIX hackers didn't much like C++/Java and wanted to do their own thing

I must say it again: C++ is a horrible language. It may perform well and it may be widely spread but that doesn't change much as far as its design is concerned. C++ competes mostly on its reach not on its merits. Hopefully that will change as new platforms emerge(i.e Rust).

> In fact Facebook itself used to transpile PHP to C++ and run that (through their HipHop project).

Yeah, that's a pretty picture for what C++ is really good for. I rest my case.

Edit: I would also say "experienced hackers" instead of "old hackers". The C++ or Swift hackers are not younger anyway.


C++ is horrible in many ways, but Go never really tried hard to replace it (although it claimed to do that in the beginning, before it was refocused as a language for backend and tools).

The are many reasons for that, but most importantly, Go doesn't support deterministic memory management (like C++, Rust and Swift) and opts for GC instead, which makes life easier for at least 95% of its use cases, but not for some of the use cases C++ used to shine on.

I think that the Plan 9 Googlers dislike of C++ (and in truth, Modern C and UNIX as well) is closer to be the real reason for creating Go. It really was never designed to be a proper system language.


>The rest of your points are invalid. Development is not only about a http server(which I'm sure is subpar compared with Go). You will burry your time and energy in making basic things work.

The same could be said, and was said for Go compared to Java C#, etc just a few years ago. And it probably can still be said, as older languages have much more stable, richer ecosystems, support, tooling, and use bases.

>I must say it again: C++ is a horrible language. It may perform well and it may be widely spread but that doesn't change much as far as its design is concerned. C++ competes mostly on its reach not on its merits. Hopefully that will change as new platforms emerge(i.e Rust).

Well, it gets a lot of things right. Compatibility with C. Great performance. Enough abstractions to be usable. Enough batteries built-in compared to C. Great ecosystem and tooling, first class vendor support, etc. A language is not just its syntax -- not even just its semantics.


>>The same could be said, and was said for Go compared to Java C#, etc just a few years ago. And it probably can still be said, as older languages have much more stable, richer ecosystems, support, tooling, and use bases.

I don't say that you should never use C++. Sometimes it's a necessary evil but that doesn't make it a great/pleasant choice(at least for me).

>> Great ecosystem and tooling

I think it could be considered GREAT 20 years ago but now it is miserable compared with the new languages. For example it has no kind of distribution model(i.e package manager). Cross platform compiling is no fun... C++ parsing is no fun either so you could say that its syntax is quite important if you were to develop new tools.


It really depends on the project. I'd prefer Clojure or Erlang for most frontends, a Lisp for the backend of an expert system, and C++ or OCaml for any heavy lifting. Python still can be a great choice for all sorts of systems too. Swift and Rust aren't quite there yet but for a startup might be worth a look in time. We're not talking about "nobody ever got fired for choosing IBM" business here and being risk-averse is itself risky.


I find swift the worst choice for a start-up right now. Why would you use a language that's only half baked? Don't you have more important things to do than porting the standard library to linux? Not to mention the lack of concurrency. Go can be a grat choice for all sort of systems too and in many cases better than any of the languages you listed. It's about using the right tool for the job and I think your comments about Go are plain ignorant.


You're going after my hyperbolic recommendation (it follows "hell,") and calling me ignorant when we merely disagree. This is a waste of time.


> Have you seen any Swift backend used in production?

Lots of iOS applications use it already.

> Would you use C++ for a web API?

Facebook and Google apparently do use it.


And Google is actively replacing a lot of those with Go because using C++ for a web api is painful.


That's a myth. Google is not actively replacing anything with Go. Some new stuff is written in Go, and a few small parts here and there were rewritten in Go during a major refactor.

There's no active program to rewrite things in Go, and most things are not meant to be written in Go from now on or anything -- it's just one of the supported languages.


Fair enough. I used poor wording there.


The only ones that Google has officially replaced has been on YouTube and downloads server.


> Most of Go's strengths shine in a large team of mediocre developers: Low build times, low abstraction, quick ramp-up, etc.

I find some of those strengths and others shine in a small team of strong developers too, if they "get" Go.

But to your point: some people expect their startups to eventually employ a large number of programmers, at which point they will inevitably have some mediocre ones, that being a side effect of "hiring only the best."

Planning for that day is not shooting yourself in the foot.


You've hit the nail on the head for me at my current place of work. The only alternative I have here is Scala, which I wouldn't choose, but higher quality ecosystem and community, and closer to Rust which I'm enjoying at home, so am thinking of doing the switch.


I cannot imagine a scenario where Go would be a better fit for a team of skilled engineers (compared to scala). Maybe ramp up time would be the only thing, but even still the dividends you can reap from scala's more complex abstraction are worth the initial investment as they are applicable to multiple projects (as opposed to learning a complex framework or whatnot).


> How do you decrement an atomic in Go?

Ooh. How do you delete from a slice in Go? With append() of course!

http://stackoverflow.com/questions/25025409/delete-element-i...


That solution (append(a[:0], a[1:]...)) doesn't look remotely performant. I don't have a copy of go handy to test, but exploding most of the list into individual arguments and passing them to a variadic function, just to pack them all back into an array, seems quite circuitous to me.

Is that really the _best_ way to delete from a slice in Go?


> Is that really the _best_ way to delete from a slice in Go?

Yep[0], and it needs a special case to remove the last element of the slice (if the index is user-provided). If you don't care about the slice order you can also swap the element you want to delete with the last element of the slice and shrink the slice by one element. Also fun, it can leak memory.

[0] https://github.com/golang/go/wiki/SliceTricks


That article is a concession speech on why generics are useful. Every single "trick" in the article is titled with the name of the equivalent method in more powerful languages. I don't think its possible to look at that list of tricks or the amount of boiler plate to get simple methods like `sort` or `keys` and assert the language is "simple" and better off for it.


Not even that, Go does have generics for its special magical builtin types (map, slice, channels, … are generic) or functions (close, delete or the aforementioned append are generic)


It doesn't explode the slice:

If the final argument is assignable to a slice type []T, it may be passed unchanged as the value for a ...T parameter if the argument is followed by .... In this case no new slice is created.

https://golang.org/ref/spec#Passing_arguments_to_..._paramet...

(Otherwise, I completely agree that having to use append to remove an element is stupid.)


Unfortunately yes. It's a great example of the sort of thing that Go does pretty badly in my experience – even though it's idiomatic, and technically performant behind the scenes, it looks immediately confusing and slow.


A slice is a view over a continguous array. deleting an index in a continguous array is inherently O(n). Go doesn't obscure this. The pain of writing it out reflects the complexity of the operation.


>The pain of writing it out reflects the complexity of the operation.

And why would one even remotely want that?

Unless they seriously think that if the syntax is painful, people will avoid doing it and get better performance...


It's because it's the wrong data-structure for the job. A slice is not sparse.

If you need to delete from the middle of an ordered collection of items then it's better to use a linked or doubly-linked list (unless it's small then it doesn't matter).


> It's because it's the wrong data-structure for the job. A slice is not sparse.

So?

> If you need to delete from the middle of an ordered collection of items then it's better to use a linked or doubly-linked list (unless it's small then it doesn't matter).

Meh. Deleting from the middle of an array is one memmove, you don't get 1~2 pointers memory overhead, caches blown and having to deref' n pointers getting to the item to remove in the first place. If your language provides arrays, there are almost no cases where you should reach for linked lists without having seriously benched both cases.

And that's before the consideration that a Go linked list means loss of type safety or a hand-rolled hand-specialized implementation.


You don't want to memmove an array which content is shared by another array, which is what slices are about.


If the order of the elements in a slice does not matter, this should be the easiest solution for deleting the element "n":

slice[n] = slice[len(slice)-1]

slice = slice[:len(slice)-1]

Probably one should campaign for adding a delete operator to slices, though so far I have never missed it.


This is a great example of the sort of thing that frustrates me when I'm using Go. Deleting an element from a slice is an obvious, common, simple operation. In order to do this, I have to build that operation from primitives, despite the fact that it will be more error-prone to do so. There are lots of other annoyances like this, too.


>and all my contacts with the community were very unpleasant

I can testify to this! I even got banned from the golang subreddit :-D

EDIT: I didn't bully anyone or anything, just told them that they can't call the language as golang, because when I had asked feedback on my work an year ago, the only feedback I got from a certain person and that too very rudely was "to not call the language Golang and call it Go, ruby is called ruby and not rubylang"


> > all my contacts with the community were very unpleasant

> I can testify to this

Even though you showed up on the golang subreddit, asked a question about javascript [1], got two answers (one without jquery, one with), you can attest to the unpleasantness of that subreddit? Even when you posted your todo manager written in Go, the majority of the feedback you got was positive.

I find your reaction a little surprising.

[1] - https://www.reddit.com/r/golang/comments/44g3ln/help_require...


You are right, by "community" I meant the core team and not the reddit community. they have been awesome (except the core team). I got a lot of positive feedback about the todo manager and the book. My apologies for the unclear comment.

When you look at the Go ecosystem there are two classes: 1. core team (the elites) 2. others (us plebeians)

The parent post has a brilliant line, which made me laugh for half hour:

>I wanted to check an invariant; how does Go do assertions? Fuck you, you’re a bad programmer. That’s how.

the others are awesome, my interaction with the core team was far from pleasant. They are rude and outright snarky. They focused on the book title more than the content and didn't even bother to read the content. I mean they did take a cursory glance, but the others on reddit were amazing:

https://www.reddit.com/r/golang/comments/3zle8c/i_am_writing...

https://groups.google.com/forum/#!searchin/golang-nuts/suraj...

This is how I got banned from the subreddit. Anyways Good for me, I stopped browsing reddit that much.

https://www.reddit.com/r/golang/comments/4lj0yn/congrats_bra...

On the last link, click the downvoted comments, I deleted few comments, but yeah, that's about it, I got banned because I questioned the elites, it is called bullying!

Later I spoke with that guy himself, turns out he wasn't being rude :-D


I think that a lot of people who like Go for its channels/coroutines would like one of the BEAM VM languages - Erlang or Elixir - with its OTP. It's a bit more heavyweight, and the community is a bit smaller than Go's, but it works nicely for lots of sorts of problems.

I'd keep using Go for one-off scripts (grab SSH keys from LDAP for sshd, that sort of thing) and system integration stuff, though.


> the community is a bit smaller than Go's

For now! Phoenix [0] will hopefully help turn the tide.

I switched from Go to Elixir/Phoenix a year ago and I couldn't be happier. I've used a lot of web frameworks over my decade as an engineer, and Phoenix is the first one that has remained fun to use, fast (!), and productive.

Crucially, its community has remained accepting and helpful under Jose's leadership - in contrast to the unfixably toxic Go community.

[0] http://www.phoenixframework.org/


what about the lack of static typing?


Elixir and Erlang both actually have static typing in the style of Facebook's Flow - type annotations can be added to your functions[0] and there's a tool, Dialyzer[1], which can be run via Mix[2] to type-check your programs.

It's not perfect - among other things, it can't type-check certain things that can and do happen in Erlang/Elixir programs, and won't complain about some errors - but it's better than being purely dynamic at not a whole lot more effort.

[0] http://elixir-lang.org/getting-started/typespecs-and-behavio... [1] http://erlang.org/doc/apps/dialyzer/dialyzer_chapter.html [2] https://github.com/jeremyjh/dialyxir


With the recent addition of better semantics for Map type specs combined with the dialyzer flags Wunderspecs, Woverspecs, and Wspecdiffs there's nothing I've found that Dialyzer misses that I'd expect a type-checker to be able to find and warn me about.


Do you find using pub/extern keywords more elegant than a simple capitalization? To me rust is verbose in many cases like java with a lot keywords heading towards C++ complexity which is exactly what Go tries to avoid. I understand that now crago becomes somehow part of the language as the extern declarations will become "optional"/deprecated. So yeah, I hope Go won't adopt any of these fancy/XXI century features.


Let me see... instead of having to capitalize every single usage of a given symbol, I can just prefix it's defintion once with `pub`? Yeap, I rather do that. Instead of replacing all instances to lower case, I can just remove the `pub`. It's obviously better.

Rust is noisy in other parts, for other reasons (trying to explain to compiler how to write super fast and data-race free code). But visibility qualifiers are not a problem.

`extern` is only for importing crates, which is like 10 lines at in whole package/crate. Irrelevant.


Just for contrast, when I'm exporting something in Go, using gorename makes it very easy to fix all the references within the package. It works the same when I'm un-exporting something as well. However, when un-exporting I have to go though all other packages that use that identifier and fix the code that used it. Presumably you need to do something similar when you remove the `pub` (i.e. It's not just a simple removing of the pub unless nothing used it to begin with).

Ultimately in the end, for this example it really seems like the same amount of work. You add one word, I run one command.


> I understand that now crago becomes somehow part of the language as the extern declarations will become "optional"/deprecated.

You are incorrect on multiple fronts - 1) there is not even an active proposal to this effect, its just an idea that was passed around and not even really discussed, 2) the idea did not involve making rustc depend on cargo at all. It would be shocking to me for the Rust community to ever accept a proposal which required rustc to depend on cargo.

In general, please try to avoid contributing to the spread of misinformation by not stating as facts things you are not very confident are true.


For your unit-testing problem, I highly recommend `testify/assert`:

https://godoc.org/github.com/stretchr/testify/assert


The fact that golang ships with a unit test library that does not contain equality assertions is aggravating. Their political stance on it is just one of many places the language designers come off less as opinionated and more as that they know my problem sets better than me.


> Contrast this with Rust whose errors read like mind-bindingly inscrutable tax forms.

It was "lol what?" for me. Rust's compiler error messages are very informative and sometimes contains tips what to change in your code to make it work (not just general words, but code you can use right now, with your variables/functions etc.).

Borrow checker explains step-by-step where ownership starts, where it ends and where you are trying to use it.

Panicking messages - another story, but it's not compiler's error messages, it's runtime. In example of code Adam using as proof, author is using 'panic' and 'unwrap' - reasons to don't expect gentle behavior.

If you can't write code in idiomatic way, try to catch your panics and give more descriptive runtime error message in your code - other users of language shouldn't pay for it.


This was where I stopped reading. The author clearly hasn't worked with Rust. Not only are the messages very descriptive, but you have an extended explaination for each error in the form of:

    rustc --explain EXXX
Which pulls up a long form explaination, and code samples of what is happening/why it is happening/how to fix it.

The blog explaining the author's disdain for Rust mostly just seems to whining about the borrow checker.


> The author clearly hasn't worked with Rust.

The author links to a previous post on the subject, from mid-2015, so they clearly have at least tried Rust in the past.

And while many Rust messages are OK, one should be careful not to confuse familiarity with Rust's error messages[0] with Rust's error messages being good (being inherently informative and good).

Here's a clearer example (fixed since): http://dbeck.github.io/My-First-Steps-In-Rust/ note two issues:

* the error message is busy as all hell which is a common issue in Rust, before you've built the habit and learned how they're structured Rust error messages often look daunting. By comparison Elm makes much more extensive use of spacing and tries to avoid repeating the same information (e.g. file names) over and over.

* the suggestion it provides is just plain wrong, it suggests implementing Debug for T, but T is always a standard number which already implement Debug, what's missing is a trait bound. To a beginner that was not a helpful suggestion as it would only send them on a wild goose chase (of trying to implement Debug on either T or i32/f32, and good luck with those).

> The blog explaining the author's disdain for Rust mostly just seems to whining about the borrow checker.

So? Just like monads in haskell, the borrow checker is hard until it "clicks" (and then it fundamentally shifts your understanding and it becomes hard to understand not getting it).

[0] and experience/understanding for the various possible sources of the most common ones, but that you can build for any compiler, even G++'s pages of template expansion garbage from the early aughts


So are you talking about error messages or about complexity of borrow-checker? Because I can agree about borrow-checker, but not about error messages (although there is a lot of noise, yes).


> So are you talking about error messages or about complexity of borrow-checker?

Er… both? In separate sections of the comment?

> Because I can [not agree] about error messages

I've provided a clear example of an issue, are you denying objective reality or are you asserting that providing incorrect suggestions leading beginners in entirely the wrong direction is fine?


So you are trying to add some negative moment about borrow-checker, when my comment is not about borrow-checker, but you need something negative. Cheap trick.


> So you are trying to add some negative moment about borrow-checker, when my comment is not about borrow-checker, but you need something negative. Cheap trick.

Now what the hell are you talking about?

I didn't reply to your comment I replied to valarauca1, they're the one who mentioned the borrow checker, if you're unhappy about that whine to them don't include me in your pity party.


Are you drunk? You are responding to the branch my comment started, comment was about error messages and your response contain words:

"And while many Rust messages are OK, one should be careful not to confuse familiarity with Rust's error messages[0] with Rust's error messages being good (being inherently informative and good)."

So you will still try to pretend we are not talking about error messages here?


> Are you drunk?

I'd appreciate you not insulting me, as well as you starting to make sense.

> You are responding to the branch my comment started

I was not, however, replying to your comment. As I already told you I was replying to valarauca1's comment, which is why my own comment was threaded below and in reply to it, and quoted it.

> So you will still try to pretend we are not talking about error messages here?

I'll repeat my previous query in a new context and with slightly more alarm: what the hell are you talking about?

I never "tried to pretend"[0] we were not talking about error messages, my answer[1] to your original query[2] specifically noted the opposite.

[0] and would again appreciate not being insulted

[1] https://news.ycombinator.com/item?id=12209010

[2] https://news.ycombinator.com/item?id=12208873


My question is not more offensive than your "what the hell you are talking about". I regret I don't have "ignore" button for talks like this.


> By comparison Elm makes much more extensive use of spacing and tries to avoid repeating the same information (e.g. file names) over and over.

Yeah, but you need that kind of repetition to get text editor jumping to correct line number.

AFAIK, Rust already implements concise messages on nightly.


> Yeah, but you need that kind of repetition to get text editor jumping to correct line number.

Not really, what you need is a regular and parseable structure.


Depends. There was a long discussion what new format would be. I am pretty sure text editor compatibility demanded filenane:row:col format, and it repeated for each displayed line.

Though to be fair I am not huge fan of Elm's one error per build either.


Bad macro matches can sometimes result in some rather obtuse error messages, and the exact root cause of some of the type mismatch errors can also be relatively obtuse - at least to a beginner.

Still way better than C or C++'s though, I suspect. (I'm too fluent in C++ errors to have a proper feel for how bad they are anymore...)


Macro errors are horrible, especially in match branching. They also become really nonsensical when you deal with built in macro's as well which have a completely different set of rules for themselves.

Iterator errors as well get massive when dealing with passing tuples down lazy lists.


> The blog explaining the author's disdain for Rust mostly just seems to whining about the borrow checker.

Most (negative) discussions about Rust are about the borrow checker. I sometimes wonder if people dislike it because it makes them feel the same way they felt the first time they learned to program and the compiler would never accept their code.


It's worth noting that the Rust project itself doesn't consider our current error setup as good as it could be. Set "RUST_NEW_ERROR_FORMAT" to get a preview of what we've been working on.

I do like the current errors, but that's no reason to rest on our laurels! There's a lot of ways in which they're not great.


The author (me) isn't a Rust expert, but has played with it a bit. Rust, I think, suffers from a lack of empathy with a novice audience. I'm a big fan of Rust and I think it introduces some novel, valuable, and fascinating new concepts. It should be taught in every CS program alongside procedural, OOP, and functional programming.

Once you understand the paradigms, Rust error messages make sense. It's like the folks who describe git by starting with a exposition on merkel trees: technically precise, but incomprehensible if you only know cvs.


  > Rust, I think, suffers from a lack of empathy with a novice audience.
I'd be interested in hearing more about this. Do you mean a novice to Rust, a novice to low-level programming, a novice to programming in general?

We're actively working on fixing up errors to be even better, so hearing about specifics here would be quite valuable!


>Rust, I think, suffers from a lack of empathy with a novice audience.

The borrow checker is the simplest thing once you internalize it's rules. Just the road to breaking all the bad habits C/C++ teach you are fine over the years takes a while. And most these rules are mostly excessive and unnecessary coming from a C perspective. The borrow checker is kind of like type systems. It prevents a whole class of errors, but you lose the ability to express some perfectly valid code.

It is really a kind of mental-quantum-leap. Once you internalize it's rules you really lose the ability to understand how other people can't.

Also to deflect some blame from The Rust-Community. I'm just kind of a horrible person on the internet. I'm working on improving :\ Sorry for the coarse comment.


Yes, agreed, the Rust error messages are very helpful. Failure to understand the messages is a lack of familiarity with Rust, not a problem with the error messages.


> Failure to understand the messages is a lack of familiarity with Rust

You could have said the exact same thing of G++'s pages of template error messages. That's indictment not praise.

If you must be highly familiar with the language to understand the compiler's error messages you might as well remove the messages altogether and just print "?" as ed(1) does, developers familiar with the language obviously don't need them and beginners apparently aren't allowed to understand them so why even bother?

(good thing the Rust developer team disagrees with your vision of the subject).


You are trying to catch on words, but it's just not true. I went my way from the 0-noob in Rust to comfortable using and I can say compiler errors were always helpful. Always. From the very beginning of my journey.


> You are trying to catch on words

No. I'm clearly replying to a clear opinion, there's no catch although I might be exaggerating very slightly.

> but it's just not true.

What is not true?

> I went my way from the 0-noob in Rust to comfortable using and I can say compiler errors were always helpful.

That's got nothing to do with the comment you're replying to.


To me, an error message should help in understanding the language better. It shouldn't impose a requirement to have the knowledge beforehand.


Each compiler error message in Rust have special code, using it you can find article with detailed explanation how things work, targeted to beginners.


That'll surely attract the beginners, such efforts by the Rust team and the whole community are truly laudable.


I like Go quite a lot. Mainly I am a Lisp programmer, so I am not looking for the next most high level programming language. I am looking for a language which gets things done, where Lisp doesn't quite fit. For jobs, which traditionally would have been done in C. Programs that run fast and have a reasonable complexity. It brings back the virtues of the Wirth computer language family back into modern times, as with strict type checking and the module system. On top of that it adds memory safetey due to the presence of GC.

Go it easy to learn, as its a moderately sized language, but it has just enough abstractions, that your programs are not held back by the lack of those, without opening the traps too "powerful" languages bring with themselves, namely complexety.

On top of all, the whole infrastructure is very well thought out and a pleasure to use. A very fast compiler which produces static executables, a good module system without header files, a rich standard library, many things. There are quite a few quirks, especially for the beginner, but the more I gain insight, the more I agree with the choices made - with some I don't, but the agree to disagree ratio is surprisingly high. And most of them don't get into the way of writing good programs, which is what counts.


> It brings back the virtues of the Wirth computer language family back into modern times, as with strict type checking and the module system. On top of that it adds memory safetey due to the presence of GC.

Wirth's work also includes languages with GC. :)


And more modern features than Go, even if designed in the 90s...


Out of genuine interest: could you elaborate on those? I have mainly worked with Pascal/Modula-2, and only shortly glanced at Oberon, so I am a bit shaky on his "later works".


I don't have much time to explain them in detail now, but can quickly provide some links:

Check Active Oberon:

http://www.ocp.inf.ethz.ch/wiki/Documentation/Language

Component Pascal is a Oberon-2 derivative but Wirth hasn't collaborated much there:

http://www.oberon.ch/blackbox.html

They made the very last version available for free. It was a kind of Delphi like, with the Oberon ideas of full stack OS.

www.pas.rochester.edu/~skulski/Presentations/BB_Class.pdf


This post is yet another reinforcement of my assertion that the people who like Go only like it because they have no idea what has happened in programming languages for the last few decades. Case in point:

> It brings back the virtues of the Wirth computer language family back into modern times, as with strict type checking and the module system.

Strict type checking? Have you used any other languages besides C? Go's type system is a joke.

Like literally, it's a joke. If you get a bunch of people who know about types languages in a room discussing types, "What about Go?" is guaranteed to get a laugh.


Sorry, your posting is unnecessarily abrasive. I am well aware about more advanced type systems. I have never claimed that Go has the most advanced type system. But indeed, compared to C/C++ which still are the most commonly used languages in industry, it has a strict type system. It could possibly have a more advanced one, but strict it is. And it gets work done.


> Sorry, your posting is unnecessarily abrasive.

Go is a much bigger problem in our industry than the tone of my post.

> I have never claimed that Go has the most advanced type system.

You claimed it had "strict type checking", when it has the least strict type system of any statically-typed language in common usage except C.

> But indeed, compared to C/C++ which still are the most commonly used languages in industry, it has a strict type system.

With template types, you can definitely get more strict type-checking out of C++ than out of Go.

So basically, Go has a more modern type system than C. And that's arguably not true. None of which disproves what I said: I said, "people who like Go only like it because they have no idea what has happened in programming languages for the last few decades."

> And it gets work done.

You can make that claim about any language. The question is, does it get work done as efficiently as other languages? And the answer is pretty clearly no.


Maybe if by "people who know about types languages" you mean "Haskell snobs"


But also ML, Scala, C++, C#, Java, TypeScript, Hack, Dart, Swift, Objective C (as of late), Visual Basic... and COBOL snobs.


I was referring more to the "joke" part of this. Just because a type system offers slightly more safety doesn't make it better. I'm primarily a Java developer, and I would prefer Go's type system over Java's the vast majority of the time. It does a much better job at staying out of the way.


That may be true, but the joke part comes when you actually want to develop new abstractions that are type-safe.

For example you cant write a thread-safe map container in Go without sacrificing type safety (i.e. one that would not crash the program when there are concurrent writes to the map).

The standard library doesn't offer one either.

This means you end up copy pasting the lock related code for every map you want to make thread safe. You have to remember to declare and initialise its lock, to use that lock every time you access it, and to remember to unlock it.

Now for some things, this cost isn't too bad. For others, it makes the language a joke.

As a result, Go is a toy, or at least, a non-general-purpose language. If the problem you're solving fits its limited set of built-in abstractions, its great. Otherwise, its a disaster.

In my experience, well written programs evolve over time to gain new carefully chosen abstractions that cleanly implement shared underlying concepts of the problem domain. These abstractions often require the use of generics.

When this happens in a Go project, however, you're out of luck. Even worse, developing primarily in Go means distorting your thinking to fit its limits.

This is more or less true in any language, and its not a binary thing. Languages are all over the place on the ladder of abstraction (with lisps being close to the top, Go near the bottom).

p.s. Java also suffers because it added generics too late: by the time they were added there was already a clear picture painted of "idiomatic Java". To make matters worse lambdas were also added way too late, and the verbosity of type declarations is still staggeringly high. However, modern Java is a much better language than Go.


I can definitely see the tradeoffs of choosing between Java's bad namespacing conventions and lack of type inference, but keep in mind you're comparing to a 20 year old language. Other languages (i.e. C#, Nim) have learned from Java's mistakes. Go hasn't; they've just gone back to making most of C's mistakes (in type systems).

I've no particular love for Java's type system, but I can't criticize it as much because it was a lot more reasonable when it was released (1996). In 2016 there's no excuse for having a type system as broken as Go's.


Or literally any statically-typed language I know of after C. I suppose it's possible there's some language I'm unaware of that has managed to somehow provide less type-checking than Go, but it's certainly not a commonly-used one.


It's not really a matter of less or more in my opinion. Java has more type checking, but it doesn't give you much more safety than Go, and it's a lot more cumbersome. In general I find Go's type system to be better than Java's because of this.


I think that Java can definitely provide significantly more protection than Go, but I do agree that Java's horrible namespacing conventions and lack of type inference makes it cumbersome.

So what, you're on par with a 20 year old language? C# for example learned from Java's mistakes; why couldn't Go?


I think I just don't come across situations where I find myself needing generics in the software that I typically write. I can imagine it would suck in those situations. There is a workaround for it, though. Basically you write a template and do code generation off of that to sort of approximate generics. I've never done it myself, though.


> I think I just don't come across situations where I find myself needing generics in the software that I typically write.

You don't need generics, sure. You can write working code in assembly, too. But abstractions catch more of your mistakes for you.

> There is a workaround for it, though. Basically you write a template and do code generation off of that to sort of approximate generics. I've never done it myself, though.

So, basically slightly more powerful macros, like in C, with most of the pitfalls and dangers associated with them.

There are better approaches to these problems.


Why is the type system a joke (genuine question)?


It provides zero abstraction, which is especially painful when handling errors. In Go you basically _have_ to handle the error right when you receive it, there is no way of usefully combining or composing it. Also error handling itself is very verbose and can't be abstracted over.

In other languages you can compose errors just as regular values, which allows you to handle the errors at the place where you have all the information to deal with it.

Not only that, the types represent the structure of the computation you ran.

For example, your type is Future[Option[List[Person]]] and just by looking at the return types of the things you called, you can tell exactly _where_ an issue occurred and how to handle it:

Your value is a Success(None)? -> The method inside your asynchronous computation that returned Option[List] failed!

Just to note, Future[Option[List[Person]]] is not some made-up example, it could be a real-world query against the database–for instance "do we know the friends of user X"?

  - Future – we run it async
  - Option – we might not know it
  - List   – his friends
This let's us very neatly tell apart things like

  - Failure – "the database response from the database timed out" 
  - Success(None) – "we have no idea about user X' friends"
  - Success(Some(Nil)) – "X has no friends"
  - Success(Some(List(...))) – "here are X' friends"
(And the compiler will make sure that you handle all possibilities)

In Go the closest pattern would likely be to extract the string from the error value and compare it to error strings you recognize. An alternative would be to define a special single-purpose data type specially for each type including all combinators, as Go would not be able to express any commonality between Future[Option[List[Person]]] and Future[Option[List[Pet]]]. You would need to define 4 new data types and all operations anew.


Mainly because it has no generics.


Nor sum types.


Interfaces can be used to messily construct open sum types. Not enough to save go's error handling story, though.


Interface values have a "type", despite being nil, that can make equality fail.


I am really curious about the kind of tasks that are easily done in Go but for which Lisp doesn't quite fit.


Sometimes I like to have a fully statically typechecked program. You can do that in Lisp, its easier in Go. Also, it directly produces statically linked executables. You can do a lot of this in SBCL, it also produces executables, has type checking etc. But for some of this, you have to wrestle a bit with the Lisp system. The Common Lisp numeric tower is great - until you want to write low level programs where you just want to have machine word sized integers which may wrap around. I once even submitted an abstract to a Lisp conference addressing some of these issues, but as it was not accepted, this side-project was postponed (but I plan to revive it some day).

And of course, there is always the question of the environment. The Go universe has tons of libraries, actively developed.


If you're looking for a FULLY statically typechecked program, you can easily do better than Go. Heck, even using Typescript or Javascript with Flow would give stronger typing than Go with its ubiquitous "interface{}".

Static executables by default is something that Go is famously good at, but I'm pretty sure Haskell can do this as well without so much pain and with a type system many times stronger than Go's.


I guess I don't write the same kind of tools as you, I never need word sized integers. Generally, I define the types according to the problem domain and don't require a specific bit layout. A different case arises when you exchange data through a given protocol, but then you don't do computation with it, just encode/decode.


Word sized integers are fast to compute. So as long as your problem can be expressed with them, it is the fastest way to perform math. When using Common Lisp, I have flexible sized integer numbers, which is nice to have, but the performance penalty can be sometimes too high.


[flagged]


Please don't do this. You obviously disagree that Go is a language which provides full static typechecking. Please instead just state that disagreement, rather than feign confusion. It leads to much better discussions.


[flagged]


HN advocates the principle of charity - assuming the best possible intentions of the people you're talking to. In this case, that would mean assuming this person means something different when they use the phrase "fully statically typechecked", stating what you mean when you say it, and possibly linking to some source material to help further explain. It's quite possible they have the same understanding you do, they just are using the terms differently.


Okay, why don't you assume in everything I'm talking about fluffy rainbow butterflies or something nice like that? Principle of charity, right? That way we can both go our separate ways happily.

If someone doesn't mean "fully statically typechecked" then they shouldn't say "fully statically typechecked". It's not an ambiguous phrase.


That is not what it means: http://philosophy.lander.edu/oriental/charity.html

People have different backgrounds and and experiences. It's reasonable for a person to think "fully statically typechecked" to mean "a program which is statically typechecked, and it does so to the entire program". That would apply to languages such as C, C++ and Go, which are statically typed, but give ways to subvert the type system. You probably mean something closer to "a program which has fully sound static typing", more along the lines of the MLs or Haskell, which have a richer type system which you cannot subvert.

We're here for interesting discussion. Derision is not a part of interesting discussion. You're relatively new here, so I figure it's worth explaining this explicitly.


> That is not what it means: http://philosophy.lander.edu/oriental/charity.html

I'm aware of what it means--I'm saying that the principle of charity doesn't apply. The principle of charity doesn't mean you get to actually change what the person is saying, it means you assume the best when there's ambiguity.

> People have different backgrounds and and experiences. It's reasonable for a person to think "fully statically typechecked" to mean "a program which is statically typechecked, and it does so to the entire program". That would apply to languages such as C, C++ and Go, which are statically typed, but give ways to subvert the type system.

But in the cases of C and Go, it's not just that--you can't use generics or templates, so you're stuck writing generic-like code with void* or object, respectively. That's not subverting the type system intentionally, it's just not type checked and there's no alternative. So these languages are not fully type checked, even if you don't subvert the type system. So even by your charitable definition, it's not fully statically type checked.

The word "fully" has a meaning that's not ambiguous. There's not an interpretation of "fully statically typechecked" that includes Go, even being charitable. Every nontrivial Go codebase has sections that are not statically typechecked and can't be modified to be statically typechecked.

> We're here for interesting discussion. Derision is not a part of interesting discussion.

I'm here for interesting discussion, and I don't think that an interesting discussion starts from obvious falsehoods like "Go is a fully statically-typed language". Derision discourages making such statements and increases the average interesting-ness of discussion because people are afraid to say dumb things. People should think before they make claims.

> You're relatively new here, so I figure it's worth explaining this explicitly.

I'm not new here, I just delete my accounts every so often.


Derision is not civil, and explicitly discouraged.


True, but given the entire point here is to have interesting conversations, I think that's a higher priority than civility.


They are considered the same thing here. There is a reason it comes first in the guidelines on comments.


Heh: my initial thought was that Go is nothing like Lisp - Lisp has the most powerful metaprogramming around thanks to its macro system, while Go doesn't even have C-level macros, or any of the type-system features some languages use as an alternative approach to metaprogramming. As a result Lisp is notoriously unopinionated, while Go is very opinionated. Lisp is functional, Go is aggressively imperative. And so on. But I guess the languages share some things: no complex type system, fast compiles, emphasis on simplicity in general, no need for an IDE, old fashioned feel (no, really, that's not a diss necessarily)... and at least Go has reflection.


> Lisp is functional

Not really, multi-paradigm.

> and at least Go has reflection.

Go is introspective.

Lisp has both introspection (e.g. find-class allows to look at existing classes) and intercession (e.g. ensure-class creates/modifies a class at runtime).


> Not really, multi-paradigm.

Yeah, that's probably a better way to describe it; it certainly ain't Haskell. But Go really doesn't want you even to use things like map and filter.


Registration is open for Startup School 2019. Classes start July 22nd.

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

Search: