Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Darker Corners of Go (rytisbiel.com)
185 points by signa11 on March 23, 2021 | hide | past | favorite | 90 comments


It looks like it has become fashionable to write Go related posts as they sure get the attention on HN :-). I don't use Go, but all these posts remind me of what Bjarne Stroustroup said [1] about C++: “There are only two kinds of languages: the ones people complain about and the ones nobody uses.” [1]: https://www.goodreads.com/quotes/226225-there-are-only-two-k...


I kind of wish people would make more posts about c++ - idiomatic c++ today is totally different than the c++ knowledge i have from pre-2010.

The fact that c++ has changed so much in the past 10 years - it’s possible to write completely type and resource safe code in c++ now - this passed me by until a year or two ago when i attended a Bjarne talk.

Also the c++ folks have written up a core guidelines doc (this was the main topic of that Bjarne talk), and it’s really good https://github.com/isocpp/CppCoreGuidelines/blob/master/CppC...


> it’s possible to write completely type and resource safe code in c++ now

"It's possible" is a rather low bar. It's also "possible" in C. Is it easy though? I wouldn't say so. Not if you push lambdas over queues to be invoked in another thread, or in the same thread, but later. Not if you take references to elements of std::vector. Not if you use std::vector<bool>. Not if you use operator[] on std::vector/std::array without tweaking some macros, because C++ committee in 2021 is still not convinced that bounds-checking is a reasonable default.

It's very easy on the other hand to write perfectly idiomatic "modern C++" which is memory-unsafe. Well... Easy if you figure out which one, among a dozen, is the right way™ to initialize a variable. (And then go and read code which initializes std::vector in all those different ways and try to guess which one does what.)


It's not that the comittee thinks bounds checking isn't a reasonable default. They can't change it because it will break existing code.


It will not break any valid program actually. I'm 97.5% sure that aborting or even throwing an exception on an out of bound vector::operator[] would be a fully conforming implementation.

The fact is that most standard library implementations can already be easily configured to bound check, so there is little pressure to mandate this in the standard.


The code, which would break, was already broken. Just in far worse ways than throwing exceptions: silent memory corruption. And there would be no backwards-compatibility breakage, since in cases, where it would throw, currently UB is triggered.


The existing code may be "broken" but it is making too much money to risk changing it for pedantic reasons.


Simply do not compile them using -std=future then.


You may want to follow the C++ subreddit, people are actually writing quite a lot about modern C++, it's just difficult to find out articles because the C++ community is completely decentralized and has no official (or even semi-official) forum. People write on their own blog and it requires some work to discover and aggregate all that content.

https://www.reddit.com/r/cpp/

I also really enjoy listening to the CppCast podcast by Rob Irving and Jason Turner, it's a nice way to follow what is happening with the language: https://cppcast.com/.


CppCast is great, most of the content is quite good and is a very good way for those of us that mostly work with other languages to still keep up with C++ progress.


It is strange that there's no official mailing list (except the defunct comp.lang.c++).

I recently wondered where one could find new C++ software since there's no announce list at all.


There is also no central authority other than a committee of decentralized interests that meet a couple of times a year to figure out how to keep their published document up to date.

One of the tricks with C++ is that it isn't a product created ex cathedra by a single interest group with a focused purpose and provided for you like mother's milk; it's a generalized idea that emerged from a bazaar of interests and provided by a free market for whatever purpose you chose. It has evolved to fill a niche rather than be designed to reach a particular goal (eg. 'kill C++') or even designed to reach a vague goal ('fix all the problems with C').

There is no official mailing list because there is no official 'there' there. C++ is not an organization, or a corporation, or a guy in his mom's basement. It's an idea held (mostly somewhat) in common by a whole lot of people with otherwise no shared interests all over the world.


Nitpick: I think that you meant "eg. 'kill C'"

(Other than that, great comment!)


The German-speaking community has an unofficial forum: https://www.c-plusplus.net/forum/


The problem is that there is even a counter movement to it, praising C with Classes kind of code, Orthodox C++.


Even 10 years ago there were two main "flavors" of C++, the generally OOP style of the original language and the very non OOP[1] style using the STL. Even within that, different shops had different subsets of the language they were willing to use. See, for example, Lakos' book Large Scale C++ Developement.

[1] Alexander Stepanov, of STL fame, hated everything OO and called it "gook". http://www.stlport.org/resources/StepanovUSA.html


> “There are only two kinds of languages: the ones people complain about and the ones nobody uses.”

Some of those languages that nobody uses, are really good, though.

Programmers that learn them hit well above their weight-class in individual-productivity, and if they're lucky they get to support applications written in "dead" languages for fat stacks. Oh and they're often fun too -- who doesn't want to have fun for the 1/3rd of their life that we're supposed to be working?

I think I understand that some people want to be popular more than they want to be good, but what I find really weird is the people who would rather be close to popular instead of close to good. What's the point?


While I also recommend checking out niche languages, the language is only one part. The platform, ecosystem is often much more important, and even in a really great PL, you won’t get an order of magnitude productivity boost over a modern high level language.

But of course, having fun is really important.


> Some of those languages that nobody uses, are really good, though.

Such as?


What a great quote. Next time my wife criticizes me, I'm going to say, "Babe, there are only two kinds of men: the ones their spouses complain about and the ones that aren't married." I'll never have to take out the trash again!


>> It looks like it has become fashionable to write Go related posts as they sure get the attention on HN

No, not really. I filter for Go, Rust, Nim, and Dart on HN. Rust is the most popular with at least a post every day or two and Go about half that time or less. There used to be way more Go articles in past years. Some of the recent Go posts have gotten traction so maybe you're seeing that.


>> it has become fashionable

> no not really

This doesn’t make sense to me at all. 50% as popular as Rust is considerably fashionable. I imagine that Rust is one of the most common programming languages to appear in post titles on HN.


I love Go and I think many people use - it works great in production environments and it's cross-platform by default. Are you insinuating it's not a widely-used language? Without going too far, Docker is written in Go.


I learned Go as my first statically typed language(Worked on ruby and some python before that). Although I missed the functional goodies from Ruby in Go, I thought that was the price I had to pay for performance and static checking guarantees.. Nevertheless, I enjoyed writing Go and its take on writing concurrent programs. As others have called it, I thought of Go as "modern C with garbage collection and first class concurrency primitives".

All this changed when I started learning Rust, man was I blown away by the language.. I never thought a statically typed language with manual memory management can feel so "scripting language" like.(Sure the borrow checker annoys you sometimes, nothing a `.clone()` can't fix when you are starting out). I truly enjoy writing Rust these days and never have I missed anything major from Ruby.

I'll be damned if I sit here and criticize Ken Thompson and Rob Pike, but I feel like in their quest to make Go "simple" they've perhaps held a little too hard on to their past.

tldr; Go feels a little too verbose and "dated" after learning rust.


I had this exact same progression, but with Python instead of Ruby. Go was a great intro to statically typed langurs though, I’d take go over JVM any day.

Currently love Rust, but Crystal, Nim, and Swift are all rather distracting.

Once Go gets generics I’ll be interested in giving it a second look.


It seems these articles appear every week now. This should be a solved problem - if you don’t like coding in Go then don’t code in Go. Simple. There is no point of writing a thirty page manifesto of everything you hate about Go. Put that effort into learning python, ruby, nodejs, or whatever floats your boat. If your tempted to write manifestos for those languages also then maybe consider a different line of work.


This article is not complaining about Go. It’s pointing out the gotchas, which every language has. IMO it reads like it was written by someone who likes Go quite a bit, or at least is as pragmatic about Go as Go is.


My favorite books about languages are the ones that point out where the language is ambiguous or otherwise has surprising, e.g C Traps and Pitfalls and Java Puzzlers: Traps, Pitfalls, and Corner Cases, . It would be great if there were programming languages with no surprises, but I don't think that it's possible. Every language has to compromise on some things in order to solve the problems it was designed for.


I haven't read the article to the end, but many of the "footguns" the author talks about could be avoided by simply reading this document: https://golang.org/doc/effective_go . It's pretty old already, but still valid, and emphasizes some of the ways Go is different from other languages. So more or less exactly what this article is doing...


More like the go tour, which is about the most basic introduction a go developer can get.


Looks pretty useless to anyone who did the Go tour and read Effective Go at least. These are just language design choices, not dark corners. It's not shooting yourself in the foot it's just doing things the Go way and there's often no other way if you want to use the language.


The author does state that the article is just a collection of potentially surprising things from other sources. They also state the target audience is those new to Go but know other languages. Title may be a bit over the top but I'll give them a pass on that as it seems to have worked.


I did the tour, studied the spec, and read Effective Go. Don’t remember the trick of constructing a set type by way of map[<settype>]struct{} in any of them.

https://rytisbiel.com/2021/03/06/darker-corners-of-go/#mapst...


Having used this trick before, it's fine. You get the uniqueness property of a set, but no easy way to do set operations, so it's not necessarily that useful.


Yup, I also think the author has a pretty low threshold for calling something a "footgun". For me, that's something that compiles fine, but blows up later at runtime, not the compiler telling you that you can't put an opening brace on its own line - that's more like a gentle slap on the wrist. But some of his other opinions are also debatable, like calling Go "not really object-oriented" because it doesn't support inheritance...


> Looks pretty useless to anyone who did the Go tour and read Effective Go at least.

Honestly? Not everyone wants (or should have) to do that in order to touch some Go code (or code in any other language). I for one am quite happy that "cheat sheets" like this one exist - it might sound paradoxical but I'm not interested in actually learning every single language I've had to tinker with, as I have plenty of other things I'd rather be doing.


> Honestly? Not everyone wants (or should have) to do that in order to touch some Go code

For hobby projects maybe, if it's part of your job you should know what you're doing and read the fine manual (basic documentation in this case).


That literally is the point, thank you for restating it explicitly. Not everybody is trying to get a job as a Go developer. People who just want to make a change or two to a tool that they use, to follow what someone else is doing, or to even just get a quick feel for the language without being treated like an absolute beginner to programming are users too and there is nothing wrong with making resources that are helpful for them.


Ideally you'd have someone more experienced to do a code review and correct your mistakes if it's just a one off change, Go is not hard to pick up and compiler gives good hints usually.


> Ideally you'd have someone more experienced to do a code review

I cannot stress enough that not all programming occurs in a work or even social context.

Besides, I did not say Go is hard to pick up. I said people should not need to read the entire manual, so to speak, to do things involving Go or any other language or tool. Cheat sheets like the linked article that outline the language in terms of "things you need to know as someone who already programs extensively" are thus actually quite helpful. That is all.


Exactly, flagged for clickbait. I (and surely many others) wouldn’t have clicked if it is more accurately titled “a random list of extremely basic golang design choices that I as a beginner find somewhat surprising, if that.”


The title is strange. This is regular intermediate level Go knowledge, not dark corners.


The entire premise of golang was that it was a "simple" language with only a few keywords and a standard that's only a few pages long. While these metrics look good on paper, they are secondary (or even less) when it comes to real world complexity and gotchas in the language. IMO, Java, even with a longer standard, is a safer and more sane language and ecosystem to use.


Almost nothing in this article is a "gotcha". It simply describes the features Go has, such as a formatter, bitwise operators, or things that Go does differently/better compared to other languages, such as strings can't be nil, break is the default in switch cases, etc.

> IMO, Java, even with a longer standard, is a safer and more sane language and ecosystem to use.

There's an entire book titled "Java Puzzlers".


Slices are definitely full of "gotcha". As is the fact that interfaces have two types of nil, type and untyped.

And the extremely leaky implementation detail of maps and slices is definitely a gotcha. Usage wise both slice and map can be nil, but map has to be make()d, for no obvious reason.

The fact that the author was confused about "strings can't be nil" shows their Java experience, I think. Coming from C++ programmer I would go "well of course it can't be nil". (But slices and maps can? Wat?)

Slices have another gotcha, described, where yes there are arrays, but you basically never use them. They're just a ghost that pops its head in and is the explanation for why your code that treated slices like lists/arrays failed.

You're (almost) only supposed to use this leaky abstraction that'll bite you if you think your "list of items" is "a list".

I say this as someone who's coded a lot of Go, and reviewed much much more: Go, like other things Rob Pike makes, only make sense and are consistent and intuitive inside his brain. They don't make objective sense.

The poster child for this is "Go doesn't have exceptions". It has "unwind the stack, call cleanup code along the way, until a handler is met. If there's no handler it'll terminate the application". In Rob Pike's brain the very definition of "exception" is something completely different from "exception".

Even the defense "we don't call them exceptions because they should not be used e.g. for failing to open a file!" is complete nonsense to anyone who's ever heard of C++. Yeah. File not found is not an exception in C++, what's your point?

That and "well OF COURSE the go compiler for ARM is called 5g and x86 is called 6g!!!".


Sure. The only thing that was obscure to me in the beginning was slices. I could not comprehend at first that an append could modify the underlying and possibly aliased data structure, and other slices. I blame the pseudo-functional use, x = append(x, ...), and the fact that taking a slice looks so much like making a copy for that. It simply deviated too much from all the other languages I've programmed in.


It's following C:

    x = realloc(x, ...)


Same here. I found it helpful to bear in mind that y := append(x, ...), while seldom wanted, is legal.


You’re reading too much into it.


The example for how to count Unicode characters (actually graphemes I think) is incorrect. It won't catch more complicated things like emojis with skin tone modifiers. Currently the best way I know to do this is to use the uniseg library.

https://github.com/rivo/uniseg


Archive, as it looks like it's being hugged to death: https://web.archive.org/web/20210322154522/https://rytisbiel...


> In the early days of Go this was a serious trap for programmers who didn’t read the instructions and relied in some way on maps being iterated in a certain order. To help spot these issues early on rather than in production, Go developers made map iterations random-ish

I didn’t know this about go specifically (as I generally expect maps to be unordered). But it is pretty funny because the Python folks did the opposite: they ensured order stability on the dict because people started relying on it.


Golang gotchas are fun :) Some of them are for beginners while others will trip you up even when you've done a lot (e.g., variable shadowing). Here's another golang gotchas post from a while back: http://devs.cloudimmunity.com/gotchas-and-common-mistakes-in...


Another underused language feature is the "reflect" package, which for me is the most powerful meta-programming tool that Go offers: It basically allows you to introspect data structures at runtime, in combination with struct tags (e.g. commonly used in the json package like `json:"foo"`) it becomes really really powerful. I've written automated database mapping / ORM code and struct deserialization code with it (e.g. here were we build a function that serializes arbitrary JSON-like data structures into deeply nested structs: https://github.com/kiprotect/go-helpers/blob/master/forms/co...), but you could do even more powerful things like completely automated generation of gRPC/REST interfaces at runtime.

Another interesting but underused capability are go plugins, which allow us to dynamically load external code at runtime. They are similar to DLLs but unfortunately not as powerful as they e.g. need to be compiled against the exact same Go version & runtime. We still use them at various points in our codebase (e.g. here: https://github.com/kiprotect/kodex/tree/master/plugins/write...).


Plugins are half-done feature, I would like for them to finish the implementation across all supported OSes and improve the way the code gets loaded as well.

Hence why they are underused.


I wouldn't say reflection is being underused.

It's just that the reflection is a powerful tool AND a necessary evil.

https://blog.golang.org/laws-of-reflection

"Once you understand these laws reflection in Go becomes much easier to use, although it remains subtle. It's a powerful tool that should be used with care and avoided unless strictly necessary."


It feels like the 'traps' rather than just things which are unique to Go should be highlighted differently, so if you're interested in the traps you can go straight to them.


Aside to the multidimensional slices thing: if you want a dense "multidimensional slice", it's probably going to be a lot more efficient (if more verbose) to allocate an underlying array by multiplying the dimensions and then make your easy to use slice-of-slices point into that array.

Note that this implies you need to avoid any operations that might resize the slices.


The title seems exaggerated, but skimming through this, it seems like a good introduction to the language. I definitely want to learn it sometime. "Slices" and "iota" seem like interesting concepts. I don't know whether they are better than usual collections and enums, but they feel different in an interesting way.


> Go programs with unused imports do not compile. This is a deliberate feature of the language as importing packages slow down the compiler.

I'm reading this and I can't shake the feeling that this is really bad compiler and/or language design.

Why would the compiler import (that is, parse and compile) a package that's unused in the code?

> A better solution is to use goimports tool. Goimports removes unreferenced imports for you. What’s even better it tries to automatically find and add missing ones.

Why can't the compiler do that? Why do you need a few dozen separate tools to complement the compiler?


>Why would the compiler import (that is, parse and compile) a package that's unused in the code?

Parse - yes, compile - no. Compiler still needs to parse your code to determine if your import is used or not.

Go team decided that compilation should take as little time as possible, so it is easier to make sure that the compiler does not have to worry about unused imports at all.

Not to mention that this keeps your code clean.

Dozen separate tools can be used effectively in your workflow (say CI\CD pipeline). Or just executed on every save. (or both)

https://golang.org/doc/faq#unused_variables_and_imports


> Compiler still needs to parse your code to determine if your import is used or not.

Parsing is easy. And it's fast. On top of that the compiler already knows it's unused:

--- start quote ---

Go programs with unused imports do not compile. This is a deliberate feature of the language as importing packages slow down the compiler.

--- end quote ---

So, the compiler has already parsed the file, and determined that the import is unused. And refuses to compile further, because... it needs to import that package anyway? Why?

> Dozen separate tools can be used effectively in your workflow

Haha, no. If you need a dozen different tools, there's no question about effectiveness at all.


> And refuses to compile further, because... it needs to import that package anyway? Why?

Because it may have side-effects, and the compiler doesn't know if it's the programmer's intention to import in order to get those side-effects, or if it's a mistake.



I'm sorry you just don't understand why Go forces you to do the right thing.


It's post-hoc justification of bad design: https://news.ycombinator.com/edit?id=26573519


Sorry to hurt your feelings, but it is not. The _compiler_ cannot know whether the code in a package might be used (e.g. because the package is just used to register some stuff; a common example being image file formats). Only the _linker_ can strip unused stuff. This has nothing to do with bad language design, most languages work that way.


> Sorry to hurt your feelings, but it is not. The _compiler_ cannot know whether the code in a package might be used

I'm sorry to hurt your feelings, but compiler definitely can know whether some external module is used in the file it's compiling now.

See my other comment: https://news.ycombinator.com/item?id=26553401


I'm not sure I follow. How can the compiler know that the import is side-effect free, transitively?

Without opening more files and parsing them, how does it know that none of the transitive dependency tree has a `func init(){}` ?

Go forces you to add an underscore if you mean "I'm importing this for its side-effects, and am not using it directly".


If it doesn’t know whether it is used or not, how can it fail a compile?


It doesn't know what the author's intention is:

"import for the side-effects", or "oops, that was a mistake".

It's not that it fails, it's that it rejects.


This just adds to my impression that Go is a very poorly designed language.

So. The compiler knows that the package is not used. How can you tell the compiler that the package is imported for side-effects?

Oh, you have to explicitly tell it to the compiler by, you know, actually using the import, right. By doing something stupid like

  import (
    _ "github.com/lib/pq"
    _ "image/png"
    ...
  )
Oh. Look. Problem solved. You mark a package as "I imported this specifically for its side effects". So the compiler can go ahead and include it.

Why does it need to include any other unused module?


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.


I’m not familiar with go, but why on Earth can an import have side-effects? That’s ridiculous..

I was never a fan of Go, but this just takes the cake..


Many languages do.

A Python import will execute anything it imports (most of what's "executed" is "def" and "class"), but if there's code at the top level, it'll run.

C++ will run constructors for global variables before main().

Even C has side-effects from linking in!

    void __attribute__ ((constructor)) init() {}
So I don't mean side-effects at compile time (e.g. it's not like C macros), but importing / linking something even if you don't use it is very common that it has side effects.

Why would you do this? Commonly to "register a handler", to not have to both "import foo" and call "foo.init()"


To be honest, I think of header files really lowly.

I do see now why would one use that, thank you! Though I still think that adding an explicit keyword to run init functions would be preferable, like `import init package`.


That's what the underscore is. :-)

Other init code is stuff like "var foo = regexp.MustCompile(...)".

Then there may be transitive imports that do C code, and it really needs to run its init code.


pretty overstated title but i guess it did catch my attention so there's that. anyways it is a nice thorough writeup of things that are different about golang. don't think they are particularly "darker" or bad or anything like that.


He lost me very early:

    "Opening bracket must be placed at the end of the line in Go. Interestingly this is not enforced by gofmt,"
Eh, no, of course it's not. It's a syntax error, fixing those are NOT the point of go fmt


[flagged]


Yes, the fact that the author knows it’s a syntax error yet still made that bizarre comment about gofmt not enforcing the style indicates that he doesn’t realize gofmt is not for fixing syntax errors (in fact, it refuses to format anything when there’s a syntax error and simply errors out, so my guess is the author only used gofmt as a formatting action in an editor like VS Code, and assumed no code change on shift+option+f means gofmt doesn’t enforce it). And that’s exactly what gp’s complaining about — demonstrated lack of understanding of gofmt.


I think that it is a syntax error is the weird part, in most languages it wouldn't be. So people stumble over an error that normally is considered a style choice.


>Operator precedence

This is a Light Corner and a Dark Corner of C.


Go, the RSL inducing language


[flagged]


I think it's a bit ridiculous how every other day the Go post on HN is just about how shit it is. And then people arguing in the comments about how "it's not simple" and bringing up things you can learn in 5 minutes.

It's just old and tiring to hear the same shit over and over again.


I barely hear a word out of go fanboys outside of their own respective bubbles. Rust fanboys however...


[flagged]


Please stop posting unsubstantive and/or flamebait comments to HN.

https://news.ycombinator.com/newsguidelines.html




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

Search: