It's pretty hilarious to watch people clambering all over each other in this thread to claim this isn't a problem because [op_is_doing_it_wrong] or [weird_hacky_workaround]. Commenting out bits of code to see what effects it has is one of the most useful ways of not only debugging a program, but learning to program, and “reading” code.
Just because some really smart people made a decision for reasons that are great 99% of the time, but fail badly in a very important 1% case doesn't mean you have to redefine your entire belief system to avoid criticizing them.
As someone who works with Go full time I can understand where this criticism is coming from, but in my experience it becomes a non-issue after very short amount of time. The various editor helpers that automatically add/remove imports help.
Not using Go here, but I configure my Java IDE to report unused imports as errors; stil not a problem because it's probably been years since I last edited import statements manually -- days can go without I even reading imports because they're usually collapsed by the editor. Non-issue.
The :Drop, :Import, :Gofmt and :Godoc statements that allow you to drop packages, import packages, cleanly format what you're working on, and document the targeted function are in the vim extras that come with Go, and work well enough to make this painless for me.
I don't know for Andrew, but I use Sublime Text 2. Cmd-., Cmd-P and type the import name, you're done (it toggles add/remove of the import). You don't have to leave your position in the file.
I usually read the first couple comments on an article before reading the article itself. I was all set to write a comment disagreeing with you after reading the first couple paragraphs, but by the end I completely agreed. Sounds like it would really disturb my flow while debugging.
I'm sure there's reasons it would be a bad idea, but having the ability to ignore errors like this when building a Debug build seems like it could be a solution.
There are plenty of workarounds. Anyway, like others I work full-time with Go and it has become an absolute non-issue for me. In 80% of the cases fmt is already imported, but in 99% of the cases I have a global variable log defined in every package anyway -- and if not I define it. (Working often test-driven, I avoid the issue totally by doing t.Logf.) So yes, every 2 months (I'm serious) I'm running into the OP's problem and it happens that I'm writing fmt.Printf("") -- but this almost exclusively happens on "Pastebin-projects".
There is great reasoning behind the concept of providing as few exceptions as possible to the users. Sometimes it means adapting your workflow/thinking, but I guess this is the case for every language if you want to become truly productive.
> It's pretty hilarious to watch people clambering all over each other in this thread to claim this isn't a problem because [op_is_doing_it_wrong] or [weird_hacky_workaround].
"Clambering" may be a bit hyperbolic, given there are only 15 comments (at the time I read this), and a handful of those propose "weird hacky workarounds" or "op is doing it wrong" comments.
Aside from that, I agree with the author that the variable not used errors are certainly irritating during a development or debug cycle. On the other hand, it is nice during a normal compile, or when using go get to pull a lib in from some other author.
There are indeed numerous work arounds, using build constraints (go build tags args) and such. Many (most?) are quite hacky and not very general case or new-developer-friendly.
It would indeed be nice to have a 'debug' compile flag that ignored such errors, if only for a little while. Maybe even make it only usable with "go build" and never "go get" or "go install".
It makes me question humanity seeing the opposite. In these comments are many "work-arounds" (this word is so negative; what about workflow-diff?) listed and I bet if you are smart enough, you can even come up with many more, more beautiful work-arounds. Using a language for one week hardly qualifies someone for qualified criticism. :-)
> Commenting out bits of code to see what effects it has is one of the most useful ways of not only debugging a program, but learning to program, and “reading” code.
I was thinking about a command line tool to enable this automatically by generating code based on the source files.
One solution to the fmt.Println problem is not to use it at all. There's a built in println function that works alright for debugging messages and requires no import.
> if something is worth warning about it's worth making it an error
Why oh why do people think this way, totally conflating things?! It makes any "explorative programming" extremely painful. Really, can't I indulge in a sloppy coding style while prototyping something, have a zillion warnings pop up (or silence them with a compiler flag), have a working prototype with ugly hacks and worst practices and then move on to slowly fix the things till I get a 0 warning 0 errors "clean state".
Not all have the god-programmer mind to start with a "good design". We grow things organically, function by function, class by class. We start from a "ball of mud" and slowly mold it into the shape of our desire (now I agree that for the different style of programming that starts from a "good design" and works hard to prevent everything from turning into a ball of mud, having straight jackets" is good, but if I don't want to live in your "cathedral asylum", then don't force me to wear one!). Every line of code is and will be rewritten time and time again. And you need warnings and errors to be very different concepts to do this style of programming!
> Really, can't I indulge in a sloppy coding style while prototyping something
I sympathise with this view, but personally, as a full-time Go programmer, when I've been hacking up little piece of throw-away or explorative code, I've generally found that not-used errors have as often been helpful as annoying.
For example, the compiler might say "i not used", and I'll think "bloody compiler, let's delete the declaration", but I'll actually find that I've named a loop variable wrongly and that this would have been a hard-to-find error requiring a few edit-run iterations, and thus I've actually saved more time by doing what the compiler asked than if I'd been able to code sloppily.
If I do find myself adding and removing many print statements from within a package, I'll sometimes add a little function:
func logf(f string, a ...interface{}) {
log.Printf(f, a...)
}
then as long as that function exists, I can add a logf call to any file in the package with no need to add and remove imports.
We can still start with mud, but it's good mud.
BTW we have warnings too, but they're generated by a different tool, go vet, which printf format checking and the like.
This. I can't tell you how often I've found that I accidentally shadowed a variable when I meant to use it, and this saves me from banging my head against the table to find the error.
In any sort of industrial setting, even with just two or three people, generally it is not possible to conflate errors and warnings, because there is no such thing as a warning. Either it breaks the compile, or it is simply ignored.
To which your response will inevitably be something like "Don't do that", which is a fine sentiment, but it doesn't work. I also feel "Don't do that", but I need to do something that will work, and making the compile break more often, before you have several tens of thousands of instances of this problem, is actually a great idea.
Any other apparent attempt to find middle ground is just a matter of moving around when the compile breaks. Either you break the build somewhere, or the warnings aren't fixed.
This is hardly surprising considering Go shares its creators with Plan 9 among other things. They have had this kind of attitude for years, see for example this thread where someone asks about some features in the Acme editor:
If anyone told me ,"write better code," in response to a question like, "does it have code folding?" I would be pretty enraged. I don't even use code folding.
To be fair, the original person was asking about the "Acme way." And the Acme way is, indeed, to just say no to a lot of the conventional wisdom about how editors should work.
Go may share some of Acme's developers, but it is a very different project to Plan 9.
I've noticed that software developers (and people in general), at the highest level, are split into three camps.
One of them takes the "nothing really matters at all" attitude (see: the majority of PHP developers). These people do not subscribe to dogma in the least but lack structure to such an extent that everything is a mess.
The next category is of the opinion that everything must be done in a certain way (the Plan 9 creators, most Java developers). This camp emphasises dogma above all else, even at the expense of common sense.
The final group, which is in the minority, consists of those who are truly pragmatic while taking the ideal into account at the same time. They tend towards perfection but know that the universe is not perfect. These are the Zen masters of development.
Most of what's built on a "nothing really matters" platform is total mess.
Most of what's built on top of a "certain way" platform works and is a good foundation to build more platforms.
Most of what's built on top of the "truly pragmatic" platform works, but is less often a foundation to build more platforms.
We need the fanatics like RMS to jumpstart GNU, and the Plan9 people to jumpstart plan9 - a lot of what you take for granted in Linux these days like namespaces, /proc, utf-8 was copied from Plan9, and for a reason: It's easier to copy features from a clean, consistent, proven implementation like Plan9 then from an inconsistent hodgepodge like the NT kernel. (And yes, I did NT kernel programming until just about the time XP came out; I say that with authority).
I do not know much at all about plan9 so I cannot comment on it but in regards to GNU -
GNU/Linux would never have gained mass adoption if certain compromises were not made. If proprietary code were completely outlawed in Linux and the GNU userland then we would not see it everywhere today.
Instead of looking at what one individual does, let's zoom out to the level of individuals working together. For an important idea to come to fruition, both sides of the coin we are talking about must invariably be addressed.
The interesting thing about us is that we can mesh our brains into bigger collective systems which in themselves act as brains - it's a fractal shape. Some individuals are capable of achieving zen on their own. Some individuals can serve as parts of a greater brain (a mass of individuals communicating with each other). That greater brain must address both sides of the coin if it is to yield something novel.
We need fanatics like RMS to jumpstart GNU but we also need pragmatists to make something truly useful of it. One does not work without the other, in both directions, in my opinion.
Thinking about it, GNU/Linux is a good example of this relationship. Stallman is an idealist and laid down very important groundwork with the GPL and GNU applications. Linus is a pragmatist and built something imperfect but extremely useful upon that foundation. If Linux did not exist then GNU would be useless (see: the HURD). If GNU & the GPL did not exist, Linux would have been bought up by a corporation and would have barely seen the light of day. The combination of idealism and pragmatism profoundly changed the world.
Of course, there does seem to be another aspect you are leaving out. It is probably easier to worry about these properties when your userbase is tiny.
Additionally, it may be easier to copy features from a "clean, consistent, proven implementation," but it is not always easy to copy features into said programs. Consider that few things are as "proven" as the Linux kernel. Or the TeX codebase, for that matter. Neither are usually considered "clean" or "consistent." While the later is seen as an interesting example of a completed code base that is no longer changing, few things have stood additions as well as the kernel.
> I'm all for opinionated language design, but when you use your compiler to cudgel your coders at the cost of productivity, you lose.
Well, the thing is that in the long run, the cost of any software system is dominated by fixing bugs with high degree of latency. Removing various classes of error by compiler fiat is an approach that is wildly unpopular but well studied.
This sentence might have been written as:
> I'm all for opinionated language design, so long as I agree with the opinion.
And that's fine. I can only speak for myself when I say that while younger me was annoyed that the damn compiler won't get out of the way, older me is annoyed that the damn compiler didn't catch this obvious defect for me.
The difference is that older me is writing less trivial programs and dealing with more existing code and writing more complicated systems.
It seems to me that the kind of people that are keen to have their compiler eliminate classes of errors in this way most likely won't be attracted to Go because of its other 'deficiencies', like null/lack of option types, lack of generics, mutability and so on. So it hits an uncomfortable middle point: 'annoying' to use, but still doesn't provide much guarantee about classes of error that most people would consider more serious than importing a module you don't use. (The latter is much easier to implement though, and that seems to have been a big driving force in various decisions about the language)
Disclaimer: I've never written a line of Go, so this is just my perception as an outsider. Go does seem to get a lot of flack from people with theoretical interest in programming languages (think LtU commenters). My speculation is that this is partly pushback against the (perceived) 'Go was created by the smartest guys in the industry' memes, along with the fact that it ignores most of the last 30 years of PL research.
Yes, I remember there was a great deal of wailing and gnashing of teeth when Go first turned up. My personal favourite was the bloke who compared Go to "Brand X" and found that the latter (which is older than C) was the better language, in his opinion [1].
I mean it's not as though using type system technology invented in the 1980s would have been such a stretch for a language created twenty-something years later.
The thing is, though, that Go has gotten lots of para-language stuff right. It ships with gofmt and inbuilt tools for manipulating code, it has decent concurrency out of the box, it compiles and runs fairly quickly. It's really meant to be a better C from a universe where C++ was never invented (I've seen it compared to Limbo, a language developed on Plan9 -- given the heritage, that would make sense).
> The thing is, though, that Go has gotten lots of para-language stuff right.
If one studies the history of programming languages, one comes to realize that this has as much or more to do with the success of a language than the language itself.
If any of the static functional languages had this same feature I'd agree - but they don't as far as I know its a warning in them. To me this isn't about "safety" its about keeping code clean. Go doesn't really put much emphasis on safety in the sense that Scala, Haskell or Rust does.
I'm all for compilers that cudgel coders, as long as they cudgel indiscriminately. It's the coddling ones that club you in the quiet moments of the night because you weren't minding your results that I despise.
Can't you just write a debug function, that checks a variable, then either does nothing or calls fmt.Println? Set the variable from a command line parameter. Sprinkle debug statements everywhere you want, and flip the parameter as needed.
Since the import is always used, it is never an error. No problem.
Yesterday I was complaining a bit about Go, so let me partially balance the scales by observing that in terms of examining this for use at my work, I absolutely love this and consider it a major plus point. Dealing with "fmt" is annoying, yes, and I sort of wish I could exclude that module from the rules. But I currently live with a huge code base in Perl, which is as far away from Go on this matter as it is possible to get, and after years of cruft buildup, it's virtually impossible to ever remove any imports from a module. You just can't tell what will happen, ever. Removing a "use X" from module Y may in fact break very-distant module Z because it happened to directly invoke X::function without ever having loaded X itself, it was accidentally depending on X being loaded by Y. Yeah, that one's easy enough to find and diagnose, but these get arbitrarily subtle.
Aside from the var _ = fmt.Println trick to avoid having to comment out the module import, maybe part of the problem is that Go is aimed a little further think-ahead-ward and a little less hack-until-it-works-ward than they're used to? Because that might be on purpose.
The OP offers up Python as an example of "weightlessness", which I take as meaning "it will execute any code it can parse." Languages with this kind of tolerance require exhausting levels of diligence to verify assumptions and ensure consistency in a development team.
Go's pedantic compiler and style enforcement fade pretty quickly into the background with practice and a reasonable text editor. The pain of trying to fix mixed metaphors between modules in Python never will.
As an aside, here is the solution to your fmt.Println problem:
"Languages with this kind of tolerance require exhausting levels of diligence"
I don't understand this statement. People successfully write large Python programs and don't get exhausted in the process. Could you elaborate on how "mixed metaphors" affects things in Python, and how Go's syntax checks eliminate that problem in Go?
I have had this problem as well and of course you can add some superfluous code that fulfills the usage for the import/variable but then you have to remember to clean up that code later (essentially discarding the very helpful checking built into the compiler).
I think they would be better off adding a compiler flag for less strict checking for development.
This is the kind of problem that can and arguably should be handled/eased by whatever editor or IDE you are using. A quick key combination to clear unused imports, another one to add required imports.
One thing I always thought would be interesting to do is make it such that the "enforced stylistic" bits of a language were tied to the optimization settings. For example, make warnings not halt the compile on the compiler's equivalent for -O0, and then on any higher optimization setting, they become compiler errors that can't be turned off. It doesn't completely prevent people from doing stupid things (e.g. deploying a debug binary to production), but it's, in my opinion, a good way of solving exactly the problem mentioned here.
I write Go every day, I was going to rant about the author being annoyed minor things (that do not annoy me conveniently), and then it struck me- I'm extremely annoyed by the author wasting a line every time he repeats his code rather than doing this (or something similar depending on case):
Then I realized, programmers as detailed people are easily annoyed, because small details often matter. But in both the case of the author, and myself here, we need to realize when it truly doesn't matter.
Sure these Go behaviours might annoy him, but they are important for large code bases and are easily worked around. Sure, repeating am overly verbose block several times annoys me, but the seven extra lines of code does not make him unreadable or incorrect-
Like most opinions about programming languages mine is highly subjective. So viewer discretion is advised:
What OP describes is exactly what drove me away from using Go for my 'high level' projects.
Now Go is a great language with great ideas but hacking something together in it is such a pain that I don't get a warm feeling when thinking about coding in Go. All those small things add up to where it just gets tiresome. Not something I want experience during programming.
There should be some kind of "I know what I'm doing"-mode that lets you import unused packages, etc.
> Now Go is a great language with great ideas but hacking something together in it is such a pain that I don't get a warm feeling when thinking about coding in Go. All those small things add up to where it just gets tiresome. Not something I want experience during programming.
This has exactly the opposite effect on me. I feel the strong grip of the compiler greatly comforting me during development. Form frees.
I've found Go to be brilliant in some areas, but very irritating in others. I've lost the link to the talk where Rob Pike describes the reasoning behind the language, but once you understand that they are optimizing for large code bases in large organizations, a lot of these irritating decisions make more sense. In this case, the OP is running up against a trade-off in short-term convenience against long term code hygiene.
yes, Go is a very radical language with very bold decisions in its design and it sure has the "Just Say No" attitude....
but to be honest, its one of the main reasons for me to love Go!! because this makes me sure that Go will not end up like C++ or other bloated languages! you turn your head around, and there is a new feature in language. that sucks.
As someone who has moved to using Go whenever it is possible and reasonably appropriate, I don't find this a particularly large problem.
Yes, it's one I encounter, but only once the package is finished and I am in the testing stage and need to debug. Then, the fmt import stays there until I'm done fixing bugs, and I just use gocode's `:Drop fmt` to drop the import. Syntastic, which is a brilliant code validator for vim uses the go compiler to point out every place I have placed an fmt.Println with >>>, allowing me to remove all of them in one fell swoop, where in other languages I would have to search through the code for various print statements. The vanilla Go compiler also points this out, as the author notes.
I prefer the way the compiler works because it makes sure your code is always representative of what needs to, and happens.
To the author I would say "Why comment out the fmt.Println if you are still debugging?" and "If you are debugging, why only one fmt.Println?".
The solution is to use gdb instead of printing variables in your code for debugging purposes. Go has great gdb support and gdb is a great debugger.
There are also multiple editors that support gdb plugins if you don't want to deal with it in its raw form which honestly isn't that bad once you're used to it.
I think I could say with a straight face that you will be able to debug a program faster with gdb than sprinkling about print statements even if you didn't have to worry about unused variables.
In other words, get used to what the language has to offer and then leverage it before saying it's annoying. The problem you have is 100% solved by gdb.
You shouldn't add code that doesn't do anything, it just creates a cognitive burden. You might be able to relieve that burden with a clever editor, but there's a clear (and useful) dividing line between code and comments that you are blurring.
I disagree that it's code. It's documentation - that can be checked by the compiler for correctness.
Yes, it is a hack, but it serves a purpose.
It's just a matter of convention; It creates as much cognitive burden as documentation does. And indeed, I avoid documentation where it isn't needed, but I do put it where it is helpful - just like I do with code that doesn't need to be built/executed as part of the product, but that helps understanding.
> It's documentation - that can be checked by the compiler for correctness.
Exactly. This was a convention used by Smalltalk shops. It came about because of the appearance of the Refactoring Browser, which caused a 10X increase in the rate and ease of refactoring. Before, Smalltalkers used to put snippets of code in comments with the exhortation to "run this" or "debug this" to clarify how a certain part of the API worked. The problem arose that refactorings would often break those snippets. However,
I can't remember its name, but there's an effect whereby syntax and style are discussed more than semantics because they are easier to talk about. This is far from the worst thing about Go.
That's a good one. I've also recalled that a field being dominated by arguments over things that are not correct or incorrect but merely matters of personal opinion and taste is a sign of a "dead field." Repeated recurrences of the same idea repackaged as the "new" thing is another sign of such.
The takeaway: One's time is better spent writing useful code.
The objection here is that "I can't hack and hack to get the answer without inconveniencing myself".
I watch people using this approach all day every day and have to sort out the shit-tip mess that is left behind because it ends up with sprawling crap.
Stop. Think. Write it once. Test it. Fix it. Done.
The "think" bit is the most important bit but is lost now that instant feedback is possible in favour of keep changing it until it looks like it works.
While this is incredibly annoying (when trying to write some toy programs in go when it first came out, the 'unused variable' thing hit me so hard until I found out about using _...) I think the 'editor plugins fix this' response is probably the best - but please comment somewhere with a list of editors/plugins that have such intelligent go support so we don't have to roll our own!
All the inconvenience will be solved if someone built a great IDE. (great like Netbeans for Java or VS for .NET :D, but with someong Gedit or Notepad is great too!)
I think we have a vim puglin function to automatically import/remove.
Good point about having to change the "_, err :=". I have found I have had to cascade changes to ":=" in go programs. I guess it does reduce the pythony feel.
The _, err cascade is a nuisance when refactoring; I have gotten into the habit of throwing a var err error at the top of any function with a number of possible error results -- it also makes a handy notation that "here there be glue code."
Of course, where there's "_, err = ...", "if err != nil" returns are rarely far behind. :)
if you use the standard if _, err := foo(); err != nil {
then the err variable's scope is limited to the if statement (usually correct), and you can then move that if statement where ever you want and it will stay intact, and will not interfere with the external code.
Yeah recently just started on my first go project.. I use hopwatch for debugging and I do end up commenting/uncommenting the module import all the time :/
I fail to see the draw to the argument that, because some people are bad developers, the rest of us should be handicapped. Making the worst developers better at the cost of making the better developers worse (productivity-wise) doesn't seem like a valid tradeoff unless the gain on the lower end far outweighs the loss on the upper end.
Go ships with a Go parser in the standard library. Among other things it is used by gofix, gofmt and go vet. People from the community have used it for writing various refactoring tools.
It's incredibly useful; I don't want to go back to a language that doesn't ship with a parser in the standard library.
I'd say the Go authors have thought about support for tooling much more than other language authors, not less.
Why do we keep upvoting these short repetitive rants about Go? There are a half a different posts with this exact same complaint. There are another dozen threads about it on the mailing list with plenty of discussion and it comes up in almost every single comment thread here.
Yes, the error handling is explicit. If this is a bother panic/defer+recover is there if you want to use it.
The case here is completely contrived; a red-herring. It's not like it's ever typical to do m, err := io.Read... and not use the `m` (if you were, you'd already be writing _, err anyway). Assuming that you write a block of code and then compile it and not write all of your code line-by-line and compile it... and then I guess try to artificially fix the compiler message instead of writing the code to utilize m first?
edit: My only point is that this isn't even an issue that arises when normally writing code; the scenario in the blog isn't one that occurs when one sits down, writes a function and then compiles it. Sorry to whoever I upset.
> The case here is completely contrived; a red-herring. It's not like it's ever typical to do m, err := io.Read... and not use the `m` (if you were, you'd already be writing _, err anyway).
I've been doing some Go recently, and I ran into this exact problem (except the rhs was something else). Ignoring the output of a not-directly-related function while debugging is quite common, in my experience. Like some others here, it didn't rise for me to the level of ranting about it, but it's interesting to see that it's not just me, so I'm glad it did for someone. :)
Just because some really smart people made a decision for reasons that are great 99% of the time, but fail badly in a very important 1% case doesn't mean you have to redefine your entire belief system to avoid criticizing them.