Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Programming is the practice of telling the computer your intentions as code.

Why are you upset that Go forces you to be explicit about your intentions, when it's ambiguous?

E.g. take C++ single-argument constructors. It's the reason we have guidelines like this: https://rules.sonarsource.com/cpp/RSPEC-1709

Go is opinionated on formatting and being explicit about unused imports. It's forcing you to do the right thing.

C++ and Python let cruft accumulate, in headers and imports, with extra tooling external to the compiler (iwyu) to detect when you made a mistake.

There are valid criticism of Go, and I have many, but this ain't one.

> Why does it need to include any other unused module?

Because without at the very least parse the entire dependency tree of the module (which could be huge) it cannot know if it's unused.

Hell, it probably NEVER is unused, because many modules will have "var ErrFoo = errors.New(...)". If it calls C code in these global variables or "func init()" then there isn't even in theory a way that the compiler can deduce that it's unused.

This is a solution to a real problem, where other languages take forever to build, and create bloated binaries.

> Why does it need to include any other unused module?

If you import it then it's not unused, and it's extremely common that that's a mistake. That's why.

Have you ever coded C++, and done a refactor? How many times do you just leave "#include <sys/types.h>" because you don't know exactly why you imported it?

Have you coded Python? Do you know that the previous author is not importing a given package for its side-effects?



> It's forcing you to do the right thing.

Mmm... No. It's just post-hoc justification of the design.

> > Why does it need to include any other unused module?

> Because without at the very least parse the entire dependency tree of the module (which could be huge) it cannot know if it's unused.

Once again: no. Because it already knows it's unused and stops the compilation. So it already knows it's unused.

> If you import it then it's not unused, and it's extremely common that that's a mistake. That's why.

Once again:

- the compiler has already parsed everything

- the compiler already knows that the import is unused

- the compiler stops the compilation at this point ... because otherwise it would have to include anyway even though at this point it already knows that the import is unused

> How many times do you just leave "#include <sys/types.h>" because you don't know exactly why you imported it?

There are other ways of dealing with this than halting the compilation entirely.


> No. It's just post-hoc justification of the design.

Do you have any reason to think that? E.g. documented or discussions at the time?

> Once again: no. Because it already knows it's unused and stops the compilation.

Hmm… ok, I see what you may be saying now. Am I right in saying that you would prefer that Go would silently ignore any non-underscore imports that aren't referenced by name?

So if I import "fmt" and don't use it, then the compiler won't even try to open the associated pkg files?

And if I want the compiler to override that (because I'm importing for the side effects), then I'll have to do the underscore trick to force the import even though it's unused?

Is that what you mean?

That seems like it would be a gotcha. As a reader of the code (not the compiler) I would then not be able to see the list of imports and actually know which ones actually get imported. Like, dammit why doesn't sql support pgsql? Imported the pgsql driver, it's right there!

If you mean "no, not silently ignore. Give a warning", then that runs into the (eyerolling) "the Go compiler doesn't have warnings. It runs effectively -Werror. It's eyerolling because it's clearly not true, as they've simply outsourced those second class warnings to govet and other tools.

But anyway, Go's forcing of this makes it so that the reader actually knows what actually gets imported.

I hope you at least recognize that because of side effects (which is true in Python (code executes on import), C++ (global variable constructors), C (attribute constructor), and others, importing and not using does not create the same behavior as not importing at all?

I don't want the compiler to make that choice for me.

If the Go compiler second-guessed an import, it would be an outlier among languages. The three others I listed don't.

And Go is baking IWYU into the compiler. You may not like it, but it's not stupid.

> There are other ways of dealing with this than halting the compilation entirely.

Oh I agree. I find the "unused variable" compile error to be hugely frustrating during development. Having to work around with "a = a" or wrapping code in "if false {}" instead of commenting out is ridiculous.

But the import one I agree with.


> Do you have any reason to think that? E.g. documented or discussions at the time?

This is directly from https://golang.org/doc/faq#unused_variables_and_imports

""" The presence of an unused variable may indicate a bug, while unused imports just slow down compilation """

Why do unused imports slow down compilation? Because that's how Go is designed. "It makes code good" is post-hoc validation of that.

> Is that what you mean?

Yes

> If you mean "no, not silently ignore. Give a warning", then that runs into the (eyerolling) "the Go compiler doesn't have warnings. It runs effectively -Werror. It's eyerolling because it's clearly not true, as they've simply outsourced those second class warnings to govet and other tools.

Yes. :) This is also how you'd find out the unused imports. The tools could even remove them for you.

> importing and not using does not create the same behavior as not importing at all?

In case of go it is the same behaviour: compilation stops. So it never bothers to import them anyway.

> I don't want the compiler to make that choice for me.

It already has made these and other multiple choices for you:

- it refuses to compile valid code

- it outsources a bunch of stuff to dozens of external tools that you still run, or are forced to run, or are supposed to run anyway. There's literally a whole parallel ecosystem in go because go generate exists.

- and I can even quote the link from above: "Nowadays, most Go programmers use a tool, goimports, which automatically rewrites a Go source file to have the correct imports, eliminating the unused imports issue in practice. This program is easily connected to most editors to run automatically when a Go source file is written."

The compiler made these choices for you. So, instead of a compiler doing its job, it requires you to run half-a-dozen or more tools just to make the compiler's job before it can even run on an otherwise absolutely valid code.

> And Go is baking IWYU into the compiler. You may not like it, but it's not stupid.

That's the other way around, isn't it? "'Include what you use' means this: for every symbol (type, function, variable, or macro) that you use in foo.cc (or foo.cpp), either foo.cc or foo.h should include a .h file that exports the declaration of that symbol."

This means there are no implicit imports like some magical global function that's part of the standard library that the compiler just knows about. And, once again. The compiler knows about unused imports so it doesn't even have to include them.

Just the fact that C++ or Python include every single thing they see an import for doesn't mean it's a good thing or that Go should do the same. Isn't it like compilers 101? Don't do more stuff than you absolutely have to do? Isn't it what (optimising) compilers ultimately do anyway: they throw away unused stuff from the generated code etc.?


> Why do unused imports slow down compilation?

Why would it not? It needs to check the tree imported for side-effects. Like I said this is true for C++, C, and Python at least too.

> "'Include what you use' means this: for every symbol (type, function, variable, or macro) that you use in foo.cc (or foo.cpp), either foo.cc or foo.h should include a .h file that exports the declaration of that symbol."

The analogy doesn't work well because "import" in Go is both a replacement for "include" and for linker flags.

Needless imports is like adding needless .o files on your linker line. The linker cannot know if you meant to call those constructor sections.

My comparison to C and C++ were not in the compiler (includes), but in the linker.

> Isn't it what (optimising) compilers ultimately do anyway: they throw away unused stuff from the generated code etc.?

Yes, and I believe linkers can trim symbols that are not referenced by any code.

But they can't remove __constructor__ sections. Because they are referenced, and will run.

E.g. maybe you imported a package because it defines some flags, but you stopped using the flag. But some scripts still provide it.

Importing not being an error means that the flag exists. Did you mean that? Go forces you to be intentional.




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

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

Search: