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

Go designers distinguish between Go language as defined by Go spec and implementation details.

//go:fix is something understood by a particular implementation of Go. Another implementation could implement Go without implementing support for //go:fix and it would be a fully compliant implementation of Go, the language.

If they made it part of the syntax, that would require other implementations to implement it.



If the comments impact correctness (which inlining doesn't, but I believe there are other directives that do), saying it's "an implementation detail" waves away "it's an implementation detail that everyone needs" aka part of the spec.

The reason it feels like a kludge is that "comments" are normally understood to be non-impactful. Is a source transformation that removes all comments valid? If comments have no impact per the spec, yes. But that's not the case here.

In practice comments in go are defined to be able to carry semantic meaning extensibly. Whether they're safe to ignore depends on what meaning is given to the directives, e.g. conditional compilation directives.


There are directives and packages that affect correctness. E.g. the embed package allows you to initialize a variable using a directive. E.g. //go:embed foo.json followed by var jsonFile string initializes the jsonFile variable with the contents of the foo.json file. A compiler or tooling that doesn't support this results in broken code.


There's nothing unique to Go about this kind of tooling. It exists in C, Java, Rust, Typescript, and probably dozens of other settings as well. It's the standard way of implementing "after-market" opt-in directives.


Are we referring to 'go fix' as after market tooling?

It's certainly done in many places. JsDoc is the biggest example I can think of. But they're all walking the line of "this doesn't have an impact, except when it does".

It being done by the language owners just makes them the ones walking the line.


That's exactly how this works: it doesn't have an impact, except when you ask it to. This is an idiomatic approach to this problem.


The part I object to is overloading comments, which aren't meant to be load bearing. A tool developed outside the language has no choice but to take something that has no meaning and overload it, but language owners weren't forced to take this route; they could've made directives not comments.

In practice, the Go language developers carved syntax out of comments, so that a comment is "anything that starts with //, unless the next characters are go:"


Actually, in practice the rule is "The delimiter for a comment is '// '", which to be clear, is slash-slash-space. Slash-slash-(not a space) is in practice a directive. There are several basic tools in the community that work on this principle including one of the basic linters.

If it would make you happier you can imagine this is part of the spec. It wouldn't change much if it was.


The book says "Comments begin with //." and "// +build" is a special comment. It was only replaced with "//go:build" 1.17 (2021) . So your statement incorrectly implied that this strict syntax distinction between directives and comments has always existed, what happened was people started doing this slowly over time and eventually noticed the disconnect and changed "// +build" which they could because all that stuff was implementation-defined behavior. Right now gofmt handles "// +build" "//+build" and "//go:build" by moving them to the top, adding //go:build if it doesn't exist and adding a space to "//+build", which already breaks setups that add a build comment.

Why would millions of programs becoming out of date with the spec make me happy. There is value in the language maintainers and go programmers talking about the same object. I don't disagree that '// ' is standard Go style (and more readable), but it would break all the code that uses //comments /// ////.

I DO agree that it wouldn't change much if by 'it' you mean the go language and it's tooling, a proper spec does prevent arbitrary change. But it should have been added at least 5 years ago.


"So your statement incorrectly implied that this strict syntax distinction between directives and comments has always existed,"

There was no such statement. "In practice" clearly indicates the contrary about it being "strict" and certainly encompasses the possibility that it only developed over time.


So how many angels can you fit on the head of a pin?


In the listed examples, the compiler will emit a diagnostic upon encountering those comments:

https://go.dev/blog/inliner#example-fixing-api-design-flaws

So these comments carry more weight than how those comment annotations might be consumed by optional tools for other languages.

For most of the listed examples, I think the corresponding C annotation would have been "[[deprecated]]", which has been added to the syntax as of C23.


It does not exist in Java. Comments in Java do not change code.


It doesn't exist in Go either. https://go.dev/ref/spec

That's why you find it in the comments. That is where tools have found a place to add their own syntax without breaking the Go code.

Absolutely you can do the same in Java. It exists to the exact same degree as it does in Go. I expect it isn't done as often in the Java world because it is much harder to parse Java code so the tools don't get built.


This also does not change th code. It is an advertisement to a linter-loke tool to take some action on the source code. Its most similar to linter directives which usually are comments.


We're talking about the "//go" comments in general I think here.

Things like "//go:embed" and "//go:build" very much do change the semantics of source code.

The comments above 'import "C"' containing C function definitions and imports change the compilation of go source code.

The "//go" comments contain a mix of ones that must be respected to compile, to being optional, to being entirely ignorable (like generate and fix).


> Things like "//go:embed" and "//go:build" very much do change the semantics of source code.

When you take the source code as a whole they do, but the Go parts are unchanged. The Go spec says nothing about //go:embed, or //go:build. These are not Go features. What you are looking at is another programming language that has been added by a particular tool, using Go comments as the gateway to supporting non-standard syntax inline.

> The "//go" comments contain a mix of ones that must be respected to compile

That is true, but in using it you've accepted that you are no longer writing Go. In fact, with respect to your talk of C, "Cgo is not Go" is one of the most oft-repeated lines seen in the Go community. It's not just a silly catchphrase. It is the technical reality. Cgo is literally a different programming language, much like how Objective-C is not C despite supporting everything C does.


> What you are looking at is another programming language that has been added by a particular tool

So the go stdlib also isn't "the go programming language"? The 'golang.org/x' repositories aren't standard go?

I feel like you're no-true-scottsmanning this into a meaningless definition where the spec defines Go, but all Go code in existence is not actually Go code, it's a different programming language which everyone happens to call Go and think of as Go.

Like, sure, redefine Go like that if you want, but I don't think anyone else in practice thinks "Ah, yes, the go stdlib isn't actually written in Go", so you'll be off by yourself with that definition.


> So the go stdlib also isn't "the go programming language"?

Which standard library? There isn't just one. tinygo's standard library, for example, isn't the same as gc's. They are, in most cases, API compatible, but there are some packages not shared and they are completely different codebases. A standard library is a distribution concern, not a language concern.

Maybe you're caught up thinking in some other kind of language that only has one distribution and trying to apply that misaligned logic to programming at large?

> I feel like you're no-true-scottsmanning this into a meaningless definition

It's not, though. It is actually something you have to think about in the real world. A 100% spec-complaint Go compiler can be completely unable to use gc's standard library code as it contains additional language constructs that are bound to gc specifically.

> I don't think anyone else in practice thinks "Ah, yes, the go stdlib isn't actually written in Go"

Again, its not clear which standard library you are referring to, but if we assume gc's it is mostly written in Go, but it contains extensions that are not Go. If you say that it was written in Go casually, people will know what you mean, but technically, no, it isn't written in Go. At least no more than an Objective-C program is written in C.

But, unlike Objective-C, the extensions are added in such a way that they remain valid Go syntactically. That means you can use, say, a Go language server on gc's standard library without it blowing up on the extensions. It gives the advantage of a new language, but without having to build new language tools.


There are no comment-based directives in Rust, are there?


It provides the feature to use. It’s possible nobody has yet.


Eh, you're right, they have a structured attribute system.


> The reason it feels like a kludge is that "comments" are normally understood to be non-impactful. Is a source transformation that removes all comments valid? If comments have no impact per the spec, yes. But that's not the case here.

This is not inlining in the compiler. It's a directive to a source transformation (refactoring) tool. So yes, this has no impact on the code. It will do things if you run `go fix` on your codebase, otherwise it won't.


And yet it still breaks "comments aren't semantic". That transformation I described is still invalid.


I don’t understand why that wouldn’t be valid. As far as I understand if you compile code with these go:fix comments, they will be ignored. But if instead of compiling the code you run ‘go fix’, the source code will be modified to inline the function call. Only after the source code has been modified in this way would compiling reflect the inlining. Do you have a different understanding?


I mean that directives other than inlining impact correctness. If you have a source file that only builds for one OS, stripping the build tag will break your build.


That's such an elegant solution.

I keep being impressed at subtle but meaningful things that Go does right.


It only seems right because there are no other implementations and the distinction is meaningless. You don't want to live in the world of multiple slightly different implementations all of which you need to support, this is, roughly, what the C++ build story looks like, and it's not fun.




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

Search: